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: hashQuery_test.go
    path of file: ./repos/acra/hmac/decryptor
    
    the code of the file until where you have to start completion: package decryptor

import (
	"bytes"
	"context"
	"fmt"
	"github.com/cossacklabs/acra/crypto"
	"github.com/cossacklabs/acra/decryptor/base"
	"github.com/cossacklabs/acra/decryptor/base/mocks"
	encryptor2 "github.com/cossacklabs/acra/encryptor"
	"github.com/cossacklabs/acra/encryptor/config"
	mocks2 "github.com/cossacklabs/acra/keystore/mocks"
	"github.com/cossacklabs/acra/sqlparser"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"testing"
)

// TestSearchablePreparedStatementsWithTextFormat process searchable 
    
func TestSearchableWithJoinsWithTextFormat(t *testing.T) {
	clientSession := &mocks.ClientSession{}
	sessionData := make(map[string]interface{}, 2)
	clientSession.On("GetData", mock.Anything).Return(func(key string) interface{} {
		return sessionData[key]
	}, func(key string) bool {
		_, ok := sessionData[key]
		return ok
	})
	clientSession.On("DeleteData", mock.Anything).Run(func(args mock.Arguments) {
		delete(sessionData, args[0].(string))
	})
	clientSession.On("SetData", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
		sessionData[args[0].(string)] = args[1]
	})

	schemaConfig := `schemas:
  - table: test_table
    columns:
      - data1
    encrypted:
      - column: data1
        searchable: true

  - table: test_table_2
    columns:
      - data1
      - data2
    encrypted:
      - column: data1
        searchable: true
      - column: data2
        searchable: true
`

	keyStore := &mocks2.ServerKeyStore{}
	keyStore.On("GetHMACSecretKey", mock.Anything).Return([]byte(`some key`), nil)
	registryHandler := crypto.NewRegistryHandler(nil)

	schema, err := config.MapTableSchemaStoreFromConfig([]byte(schemaConfig), config.UseMySQL)
	if err != nil {
		t.Fatal(err)
	}

	type testcase struct {
		Query string
	}

	testcases := []testcase{
		{Query: "SELECT * FROM table1 t1 inner join test_table t2 inner join test_table_2 t3 on t2.data1=t3.data1"},
		{Query: "SELECT * FROM table1 inner join test_table inner join test_table_2 on test_table.data1=test_table_2.data1"},
		{Query: "SELECT * FROM table1 inner join test_table t1 inner join test_table_2 on t1.data1=test_table_2.data1"},
		{Query: "SELECT * FROM test_table as t1 join some_table_1 join some_table_2 join test_table_2 on t1.data1=test_table_2.data1"},
		{Query: "SELECT * FROM table1 t1 inner join test_table_2 t3 on t3.data1='some_data'"},
		{Query: "SELECT * FROM test_table_2 inner join table1 t2 on data2='some_data'"},
		{Query: "SELECT * FROM test_table inner join test_table_2 t2 on data1='some_data'"},
		{Query: "SELECT value1 FROM test_table t1, test_table_2 where t1.data1='some_data'"},
		{Query: "SELECT value1 FROM test as tt, test_table_2 t2, test_table where data2='some_data'"},
	}

	encryptors := []*HashQuery{NewMysqlHashQuery(keyStore, schema, registryHandler)}
	for _, encryptor := range encryptors {
		for _, tcase := range testcases {
			ctx := base.SetClientSessionToContext(context.Background(), clientSession)
			parser := sqlparser.New(sqlparser.ModeDefault)

			queryObj := base.NewOnQueryObjectFromQuery(tcase.Query, parser)
			queryObj, _, err = encryptor.OnQuery(ctx, queryObj)
			assert.NoError(t, err)

			stmt, err := queryObj.Statement()
			assert.NoError(t, err)

			var whereExps = make([]*sqlparser.Where, 0)
			err = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
				switch nodeType := node.(type) {
				case *sqlparser.Where:
					whereExps = append(whereExps, nodeType)
				case sqlparser.JoinCondition:
					whereExps = append(whereExps, &sqlparser.Where{
						Type: "on",
						Expr: nodeType.On,
					})
				}
				return true, nil
			}, stmt)

			for _, whereExp := range whereExps {
				if whereExp == nil {
					continue
				}

				switch expr := whereExp.Expr.(type) {
				case *sqlparser.ComparisonExpr:

					if _, ok := encryptor.coder.(*encryptor2.MysqlDBDataCoder); ok && whereExp.Type == sqlparser.WhereStr {
						convertExpr, ok := expr.Left.(*sqlparser.ConvertExpr)
						assert.True(t, ok)

						_, ok = convertExpr.Expr.(*sqlparser.SubstrExpr)
						assert.True(t, ok)
					}

					switch expr := expr.Right.(type) {
					case *sqlparser.SQLVal:
						// if RightExpr is SQLVal check weather its hash
						assert.True(t, len(expr.Val) == 68, "expect replacing value on substring with hash `%s`", queryObj.Query())
					case *sqlparser.SubstrExpr:
						assert.Equal(t, sqlparser.String(expr.Name.Name), "data1")
					}
				}
			}
		}
	}
}