You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: hash_test.go
    path of file: ./repos/rueidis/om
    
    the code of the file until where you have to start completion: package om

import (
	"context"
	"encoding/json"
	"math/rand"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/oklog/ulid/v2"
	"github.com/redis/rueidis"
)

type HashTestStruct struct {
	Key   string `redis:",key"`
	F2    *bool
	F2N   *bool
	F3    *string
	F3N   *string
	F4    *int64
	F4N   *int64
	Val   []byte
	Ver   int64 `redis:",ver"`
	F1    bool
	F5    *bool
	Vec32 []float32
	Vec64 []float64
	JSON  json.RawMessage
}

type Unsupported struct {
	Key string `redis:",key"`
	Ver int64  `redis:",ver"`
	F1  int32
}

type Mismatch struct {
	Key string `redis:",key"`
	Ver int64  `redis:",ver"`
	F1  int64
	F2  *int64
}

func TestNewHashRepositoryPanic(t *testing.T) {
	if v := recovered(func() {
		NewHashRepository("hash", Unsupported{}, nil)
	}); !strings.Contains(v, "should not contain unsupported field type") {
		t.Fatalf("unexpeceted message %v", v)
	}
}

func TestNewHashRepositoryMismatch(t *testing.T) {
	ctx := context.Background()

	client := setup(t)
	client.Do(ctx, client.B().Flushall().Build())
	defer client.Close()

	repo := NewHashRepository("hashmismatch", Mismatch{}, client)
	if err := repo.CreateIndex(ctx, func(schema FtCreateSchema) rueidis.Completed {
		return schema.FieldName("F1").Tag().Build()
	}); err != nil {
		t.Fatal(err)
	}

	t.Run("Mismatch int64", func(t *testing.T) {
		e := repo.NewEntity()
		if err := repo.Save(ctx, e); err != nil {
			t.Fatal(err)
		}
		if err := client.Do(ctx, client.B().Hmset().Key("hashmismatch:"+e.Key).FieldValue().FieldValue("F1", "").Build()).Error(); err != nil {
			t.Fatal(err)
		}
		if _, err := repo.Fetch(ctx, e.Key); err == nil {
			t.Fatal("Fetch not failed as expected")
		}
		if _, _, err := repo.Search(ctx, func(search FtSearchIndex) rueidis.Completed {
			return search.Query("*").Build()
		}); err == nil {
			t.Fatal("Search not failed as expected")
		}
	})

	t.Run("Mismatch *int64", func(t *testing.T) {
		e := repo.NewEntity()
		if err := repo.Save(ctx, e); err != nil {
			t.Fatal(err)
		}
		if err := client.Do(ctx, client.B().Hmset().Key("hashmismatch:"+e.Key).FieldValue().FieldValue("F2", "").Build()).Error(); err != nil {
			t.Fatal(err)
		}
		if _, err := repo.Fetch(ctx, e.Key); err == nil {
			t.Fatal("Fetch not failed as expected")
		}
		if _, _, err := repo.Search(ctx, func(search FtSearchIndex) rueidis.Completed {
			return search.Query("*").Build()
		}); err 
    
func TestNewHashRepository(t *testing.T) {
	ctx := context.Background()

	client := setup(t)
	client.Do(ctx, client.B().Flushall().Build())
	defer client.Close()

	repo := NewHashRepository("hash", HashTestStruct{}, client)

	t.Run("NewEntity", func(t *testing.T) {
		e := repo.NewEntity()
		ulid.MustParse(e.Key)
	})

	t.Run("Save", func(t *testing.T) {
		f4 := rand.Int63()
		e := repo.NewEntity()
		f := false
		// test save
		e.Val = []byte("any")
		e.F1 = true
		e.F2 = &e.F1
		e.F3 = &e.Key
		e.F4 = &f4
		e.F5 = &f
		e.Vec32 = []float32{3, 2, 1}
		e.Vec64 = []float64{1, 2, 3}
		e.JSON = []byte(`[1]`)
		if err := repo.Save(ctx, e); err != nil {
			t.Fatal(err)
		}
		if e.Ver != 1 {
			t.Fatalf("ver should be increment")
		}

		// test ErrVersionMismatch
		e.Ver = 0
		if err := repo.Save(ctx, e); err != ErrVersionMismatch {
			t.Fatalf("save should fail if ErrVersionMismatch, got: %v", err)
		}
		e.Ver = 1 // restore

		t.Run("Fetch", func(t *testing.T) {
			ei, err := repo.Fetch(ctx, e.Key)
			if err != nil {
				t.Fatal(err)
			}
			if e == ei {
				t.Fatalf("e's address should not be the same as ee's")
			}
			if !reflect.DeepEqual(e, ei) {
				t.Fatalf("e should be the same as ee")
			}
		})

		t.Run("FetchCache", func(t *testing.T) {
			ei, err := repo.FetchCache(ctx, e.Key, time.Minute)
			if err != nil {
				t.Fatal(err)
			}
			if e == ei {
				t.Fatalf("e's address should not be the same as ee's")
			}
			if !reflect.DeepEqual(e, ei) {
				t.Fatalf("ee should be the same as e")
			}
		})

		t.Run("Search", func(t *testing.T) {
			err := repo.CreateIndex(ctx, func(schema FtCreateSchema) rueidis.Completed {
				return schema.FieldName("Val").Text().Build()
			})
			time.Sleep(time.Second)
			if err != nil {
				t.Fatal(err)
			}
			n, records, err := repo.Search(ctx, func(search FtSearchIndex) rueidis.Completed {
				return search.Query("*").Build()
			})
			if err != nil {
				t.Fatal(err)
			}
			if n != 1 {
				t.Fatalf("unexpected total count %v", n)
			}
			if len(records) != 1 {
				t.Fatalf("unexpected return count %v", n)
			}
			if !reflect.DeepEqual(e, records[0]) {
				t.Fatalf("items[0] should be the same as e")
			}
			if err = repo.DropIndex(ctx); err != nil {
				t.Fatal(err)
			}
		})

		t.Run("Search Sort", func(t *testing.T) {
			err := repo.CreateIndex(ctx, func(schema FtCreateSchema) rueidis.Completed {
				return schema.FieldName("Val").Text().Sortable().Build()
			})
			time.Sleep(time.Second)
			if err != nil {
				t.Fatal(err)
			}
			n, records, err := repo.Search(ctx, func(search FtSearchIndex) rueidis.Completed {
				return search.Query("*").Sortby("Val").Build()
			})
			if err != nil {
				t.Fatal(err)
			}
			if n != 1 {
				t.Fatalf("unexpected total count %v", n)
			}
			if len(records) != 1 {
				t.Fatalf("unexpected return count %v", n)
			}
			if !reflect.DeepEqual(e, records[0]) {
				t.Fatalf("items[0] should be the same as e")
			}
			if err = repo.DropIndex(ctx); err != nil {
				t.Fatal(err)
			}
		})

		t.Run("Delete", func(t *testing.T) {
			if err := repo.Remove(ctx, e.Key); err != nil {
				t.Fatal(err)
			}
			ei, err := repo.Fetch(ctx, e.Key)
			if !IsRecordNotFound(err) {
				t.Fatalf("should not be found, but got %v", ei)
			}
			_, err = repo.FetchCache(ctx, e.Key, time.Minute)
			if !IsRecordNotFound(err) {
				t.Fatalf("should not be found, but got %v", e)
			}
		})
	})

	t.Run("SaveMulti", func(t *testing.T) {
		entities := []*HashTestStruct{
			repo.NewEntity(),
			repo.NewEntity(),
			repo.NewEntity(),
		}

		for _, e := range entities {
			e.Val = []byte("any")
		}

		for i, err := range repo.SaveMulti(context.Background(), entities...) {
			if err != nil {
				t.Fatal(err)
			}
			if entities[i].Ver != 1 {
				t.Fatalf("unexpected ver %d", entities[i].Ver)
			}
		}

		entities[len(entities)-1].Ver = 0

		for i, err := range repo.SaveMulti(context.Background(), entities...) {
			if i == len(entities)-1 {
				if err != ErrVersionMismatch {
					t.Fatalf("unexpected err %v", err)
				}
			} else {
				if err != nil {
					t.Fatal(err)
				}
				if entities[i].Ver != 2 {
					t.Fatalf("unexpected ver %d", entities[i].Ver)
				}
			}
		}
	})
}