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: tree_test.go
    path of file: ./repos/cosmos-sdk/store/commitment/iavl
    
    the code of the file until where you have to start completion: package iavl

import (
	"testing"

	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"

	"cosmossdk.io/log"
	"cosmossdk.io/store/v2"
	"cosmossdk.io/store/v2/commitment"
	dbm "cosmossdk.io/store/v2/db"
)

func TestCommitterSuite(t *testing.T) {
	s := &commitment.CommitStoreTestSuite{
		NewStore: func(db store.RawDB, storeKeys []string, pruneOpts *store.PruneOptions, logger log.Logger) (*commitment.CommitStore, error) {
			multiTrees := make(map[string]commitment.Tree)
			cfg := DefaultConfig()
			for _, storeKey := range storeKey
    
func TestIavlTree(t *testing.T) {
	// generate a new tree
	tree := generateTree()
	require.NotNil(t, tree)

	initVersion := tree.GetLatestVersion()
	require.Equal(t, uint64(0), initVersion)

	// write a batch of version 1
	require.NoError(t, tree.Set([]byte("key1"), []byte("value1")))
	require.NoError(t, tree.Set([]byte("key2"), []byte("value2")))
	require.NoError(t, tree.Set([]byte("key3"), []byte("value3")))

	workingHash := tree.WorkingHash()
	require.NotNil(t, workingHash)
	require.Equal(t, uint64(0), tree.GetLatestVersion())

	// commit the batch
	commitHash, version, err := tree.Commit()
	require.NoError(t, err)
	require.Equal(t, version, uint64(1))
	require.Equal(t, workingHash, commitHash)
	require.Equal(t, uint64(1), tree.GetLatestVersion())

	// ensure we can get expected values
	bz, err := tree.Get(1, []byte("key1"))
	require.NoError(t, err)
	require.Equal(t, []byte("value1"), bz)

	bz, err = tree.Get(2, []byte("key1"))
	require.Error(t, err)
	require.Nil(t, bz)

	// write a batch of version 2
	require.NoError(t, tree.Set([]byte("key4"), []byte("value4")))
	require.NoError(t, tree.Set([]byte("key5"), []byte("value5")))
	require.NoError(t, tree.Set([]byte("key6"), []byte("value6")))
	require.NoError(t, tree.Remove([]byte("key1"))) // delete key1
	version2Hash := tree.WorkingHash()
	require.NotNil(t, version2Hash)
	commitHash, version, err = tree.Commit()
	require.NoError(t, err)
	require.Equal(t, version, uint64(2))
	require.Equal(t, version2Hash, commitHash)

	// get proof for key1
	proof, err := tree.GetProof(1, []byte("key1"))
	require.NoError(t, err)
	require.NotNil(t, proof.GetExist())

	proof, err = tree.GetProof(2, []byte("key1"))
	require.NoError(t, err)
	require.NotNil(t, proof.GetNonexist())

	// write a batch of version 3
	require.NoError(t, tree.Set([]byte("key7"), []byte("value7")))
	require.NoError(t, tree.Set([]byte("key8"), []byte("value8")))
	require.NoError(t, err)
	_, _, err = tree.Commit()
	require.NoError(t, err)

	// prune version 1
	err = tree.Prune(1)
	require.NoError(t, err)
	require.Equal(t, uint64(3), tree.GetLatestVersion())
	err = tree.LoadVersion(1)
	require.Error(t, err)

	// load version 2
	err = tree.LoadVersion(2)
	require.NoError(t, err)
	require.Equal(t, version2Hash, tree.WorkingHash())

	// close the db
	require.NoError(t, tree.Close())
}