ksql/kstructs/testhelpers.go

126 lines
3.0 KiB
Go

package kstructs
import (
"fmt"
"reflect"
"github.com/pkg/errors"
)
// FillStructWith is meant to be used on unit tests to mock
// the response from the database.
//
// The first argument is any struct you are passing to a ksql func,
// and the second is a map representing a database row you want
// to use to update this struct.
func FillStructWith(record interface{}, dbRow map[string]interface{}) error {
v := reflect.ValueOf(record)
t := v.Type()
if t.Kind() != reflect.Ptr {
return fmt.Errorf(
"FillStructWith: expected input to be a pointer to struct but got %T",
record,
)
}
t = t.Elem()
v = v.Elem()
if t.Kind() != reflect.Struct {
return fmt.Errorf(
"FillStructWith: expected input kind to be a struct but got %T",
record,
)
}
info := GetTagInfo(t)
for colName, rawSrc := range dbRow {
fieldInfo := info.ByName(colName)
if !fieldInfo.Valid {
// Ignore columns not tagged with `ksql:"..."`
continue
}
src := NewPtrConverter(rawSrc)
dest := v.Field(fieldInfo.Index)
destType := t.Field(fieldInfo.Index).Type
destValue, err := src.Convert(destType)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("FillStructWith: error on field `%s`", colName))
}
dest.Set(destValue)
}
return nil
}
// FillSliceWith is meant to be used on unit tests to mock
// the response from the database.
//
// The first argument is any slice of structs you are passing to a ksql func,
// and the second is a slice of maps representing the database rows you want
// to use to update this struct.
func FillSliceWith(entities interface{}, dbRows []map[string]interface{}) error {
sliceRef := reflect.ValueOf(entities)
sliceType := sliceRef.Type()
if sliceType.Kind() != reflect.Ptr {
return fmt.Errorf(
"FillSliceWith: expected input to be a pointer to a slice of structs but got %v",
sliceType,
)
}
structType, isSliceOfPtrs, err := DecodeAsSliceOfStructs(sliceType.Elem())
if err != nil {
return errors.Wrap(err, "FillSliceWith")
}
slice := sliceRef.Elem()
for idx, row := range dbRows {
if slice.Len() <= idx {
var elemValue reflect.Value
elemValue = reflect.New(structType)
if !isSliceOfPtrs {
elemValue = elemValue.Elem()
}
slice = reflect.Append(slice, elemValue)
}
err := FillStructWith(slice.Index(idx).Addr().Interface(), row)
if err != nil {
return errors.Wrap(err, "FillSliceWith")
}
}
sliceRef.Elem().Set(slice)
return nil
}
// CallFunctionWithRows was created for helping test the QueryChunks method
func CallFunctionWithRows(fn interface{}, rows []map[string]interface{}) error {
fnValue := reflect.ValueOf(fn)
chunkType, err := ParseInputFunc(fn)
if err != nil {
return err
}
chunk := reflect.MakeSlice(chunkType, 0, len(rows))
// Create a pointer to a slice (required by FillSliceWith)
chunkPtr := reflect.New(chunkType)
chunkPtr.Elem().Set(chunk)
err = FillSliceWith(chunkPtr.Interface(), rows)
if err != nil {
return err
}
err, _ = fnValue.Call([]reflect.Value{chunkPtr.Elem()})[0].Interface().(error)
return err
}