sqlboiler/boil/reflect_test.go
Aaron L 32b5952c96 Make sure we pass around the addr of slices.
- Use reflect.Indirect instead of .Elem()
2016-08-29 22:49:02 -07:00

618 lines
13 KiB
Go

package boil
import (
"database/sql/driver"
"reflect"
"testing"
"time"
"gopkg.in/DATA-DOG/go-sqlmock.v1"
"gopkg.in/nullbio/null.v4"
)
type mockRowMaker struct {
int
rows []driver.Value
}
func TestBind(t *testing.T) {
t.Parallel()
testResults := []*struct {
ID int
Name string `boil:"test"`
}{}
query := &Query{
from: []string{"fun"},
}
db, mock, err := sqlmock.New()
if err != nil {
t.Error(err)
}
ret := sqlmock.NewRows([]string{"id", "test"})
ret.AddRow(driver.Value(int64(35)), driver.Value("pat"))
ret.AddRow(driver.Value(int64(12)), driver.Value("cat"))
mock.ExpectQuery(`SELECT \* FROM "fun";`).WillReturnRows(ret)
SetExecutor(query, db)
err = query.Bind(&testResults)
if err != nil {
t.Error(err)
}
if len(testResults) != 2 {
t.Fatal("wrong number of results:", len(testResults))
}
if id := testResults[0].ID; id != 35 {
t.Error("wrong ID:", id)
}
if name := testResults[0].Name; name != "pat" {
t.Error("wrong name:", name)
}
if id := testResults[1].ID; id != 12 {
t.Error("wrong ID:", id)
}
if name := testResults[1].Name; name != "cat" {
t.Error("wrong name:", name)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Error(err)
}
}
func TestGetBoilTag(t *testing.T) {
type TestStruct struct {
FirstName string `boil:"test_one,boil"`
LastName string `boil:"test_two"`
MiddleName string `boil:"middle_name,boil"`
AwesomeName string `boil:"awesome_name"`
Age string `boil:",boil"`
Face string `boil:"-"`
Nose string
}
var testTitleCases = map[string]string{
"test_one": "TestOne",
"test_two": "TestTwo",
"middle_name": "MiddleName",
"awesome_name": "AwesomeName",
"age": "Age",
"face": "Face",
"nose": "Nose",
}
var structFields []reflect.StructField
typ := reflect.TypeOf(TestStruct{})
removeOk := func(thing reflect.StructField, ok bool) reflect.StructField {
if !ok {
panic("Exploded")
}
return thing
}
structFields = append(structFields, removeOk(typ.FieldByName("FirstName")))
structFields = append(structFields, removeOk(typ.FieldByName("LastName")))
structFields = append(structFields, removeOk(typ.FieldByName("MiddleName")))
structFields = append(structFields, removeOk(typ.FieldByName("AwesomeName")))
structFields = append(structFields, removeOk(typ.FieldByName("Age")))
structFields = append(structFields, removeOk(typ.FieldByName("Face")))
structFields = append(structFields, removeOk(typ.FieldByName("Nose")))
expect := []struct {
Name string
Recurse bool
}{
{"TestOne", true},
{"TestTwo", false},
{"MiddleName", true},
{"AwesomeName", false},
{"Age", true},
{"-", false},
{"Nose", false},
}
for i, s := range structFields {
name, recurse := getBoilTag(s, testTitleCases)
if expect[i].Name != name {
t.Errorf("Invalid name, expect %q, got %q", expect[i].Name, name)
}
if expect[i].Recurse != recurse {
t.Errorf("Invalid recurse, expect %v, got %v", !recurse, recurse)
}
}
}
func TestBindSingular(t *testing.T) {
t.Parallel()
testResults := struct {
ID int
Name string `boil:"test"`
}{}
query := &Query{
from: []string{"fun"},
}
db, mock, err := sqlmock.New()
if err != nil {
t.Error(err)
}
ret := sqlmock.NewRows([]string{"id", "test"})
ret.AddRow(driver.Value(int64(35)), driver.Value("pat"))
mock.ExpectQuery(`SELECT \* FROM "fun";`).WillReturnRows(ret)
SetExecutor(query, db)
err = query.Bind(&testResults)
if err != nil {
t.Error(err)
}
if id := testResults.ID; id != 35 {
t.Error("wrong ID:", id)
}
if name := testResults.Name; name != "pat" {
t.Error("wrong name:", name)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Error(err)
}
}
var loadFunctionCalled bool
var loadFunctionNestedCalled int
type testRStruct struct {
}
type testNestedStruct struct {
ID int
R *testNestedRStruct
}
type testNestedRStruct struct {
ToEagerLoad *testNestedStruct
}
type testNestedSlice struct {
ID int
R *testNestedRSlice
}
type testNestedRSlice struct {
ToEagerLoad []*testNestedSlice
}
func (r *testRStruct) LoadTestOne(exec Executor, singular bool, obj interface{}) error {
loadFunctionCalled = true
return nil
}
func (r *testNestedRStruct) LoadToEagerLoad(exec Executor, singular bool, obj interface{}) error {
switch x := obj.(type) {
case *testNestedStruct:
x.R = &testNestedRStruct{
&testNestedStruct{ID: 5},
}
case *[]*testNestedStruct:
for _, r := range *x {
r.R = &testNestedRStruct{
&testNestedStruct{ID: 5},
}
}
}
loadFunctionNestedCalled++
return nil
}
func (r *testNestedRSlice) LoadToEagerLoad(exec Executor, singular bool, obj interface{}) error {
switch x := obj.(type) {
case *testNestedSlice:
x.R = &testNestedRSlice{
[]*testNestedSlice{&testNestedSlice{ID: 5}},
}
case *[]*testNestedSlice:
for _, r := range *x {
r.R = &testNestedRSlice{
[]*testNestedSlice{&testNestedSlice{ID: 5}},
}
}
}
loadFunctionNestedCalled++
return nil
}
func TestLoadRelationshipsSlice(t *testing.T) {
// t.Parallel() Function uses globals
loadFunctionCalled = false
testSlice := []*struct {
ID int
R *testRStruct
}{}
if err := loadRelationships(nil, []string{"TestOne"}, &testSlice, false); err != nil {
t.Error(err)
}
if !loadFunctionCalled {
t.Errorf("Load function was not called for testSlice")
}
}
func TestLoadRelationshipsSingular(t *testing.T) {
// t.Parallel() Function uses globals
loadFunctionCalled = false
testSingular := struct {
ID int
R *testRStruct
}{}
if err := loadRelationships(nil, []string{"TestOne"}, &testSingular, true); err != nil {
t.Error(err)
}
if !loadFunctionCalled {
t.Errorf("Load function was not called for singular")
}
}
func TestLoadRelationshipsSliceNested(t *testing.T) {
// t.Parallel() Function uses globals
testSlice := []*testNestedStruct{
{
ID: 5,
},
}
loadFunctionNestedCalled = 0
if err := loadRelationships(nil, []string{"ToEagerLoad", "ToEagerLoad", "ToEagerLoad"}, &testSlice, false); err != nil {
t.Error(err)
}
if loadFunctionNestedCalled != 3 {
t.Error("Load function was called:", loadFunctionNestedCalled, "times")
}
testSliceSlice := []*testNestedSlice{
{
ID: 5,
},
}
loadFunctionNestedCalled = 0
if err := loadRelationships(nil, []string{"ToEagerLoad", "ToEagerLoad", "ToEagerLoad"}, &testSliceSlice, false); err != nil {
t.Error(err)
}
if loadFunctionNestedCalled != 3 {
t.Error("Load function was called:", loadFunctionNestedCalled, "times")
}
}
func TestLoadRelationshipsSingularNested(t *testing.T) {
// t.Parallel() Function uses globals
testSingular := testNestedStruct{
ID: 5,
}
loadFunctionNestedCalled = 0
if err := loadRelationships(nil, []string{"ToEagerLoad", "ToEagerLoad", "ToEagerLoad"}, &testSingular, true); err != nil {
t.Error(err)
}
if loadFunctionNestedCalled != 3 {
t.Error("Load function was called:", loadFunctionNestedCalled, "times")
}
testSingularSlice := testNestedSlice{
ID: 5,
}
loadFunctionNestedCalled = 0
if err := loadRelationships(nil, []string{"ToEagerLoad", "ToEagerLoad", "ToEagerLoad"}, &testSingularSlice, true); err != nil {
t.Error(err)
}
if loadFunctionNestedCalled != 3 {
t.Error("Load function was called:", loadFunctionNestedCalled, "times")
}
}
func TestBind_InnerJoin(t *testing.T) {
t.Parallel()
testResults := []*struct {
Happy struct {
ID int `boil:"identifier"`
} `boil:",bind"`
Fun struct {
ID int `boil:"id"`
} `boil:",bind"`
}{}
query := &Query{
from: []string{"fun"},
joins: []join{{kind: JoinInner, clause: "happy as h on fun.id = h.fun_id"}},
}
db, mock, err := sqlmock.New()
if err != nil {
t.Error(err)
}
ret := sqlmock.NewRows([]string{"id"})
ret.AddRow(driver.Value(int64(10)))
ret.AddRow(driver.Value(int64(11)))
mock.ExpectQuery(`SELECT "fun"\.\* FROM "fun" INNER JOIN happy as h on fun.id = h.fun_id;`).WillReturnRows(ret)
SetExecutor(query, db)
err = query.Bind(&testResults)
if err != nil {
t.Error(err)
}
if len(testResults) != 2 {
t.Fatal("wrong number of results:", len(testResults))
}
if id := testResults[0].Happy.ID; id != 0 {
t.Error("wrong ID:", id)
}
if id := testResults[0].Fun.ID; id != 10 {
t.Error("wrong ID:", id)
}
if id := testResults[1].Happy.ID; id != 0 {
t.Error("wrong ID:", id)
}
if id := testResults[1].Fun.ID; id != 11 {
t.Error("wrong ID:", id)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Error(err)
}
}
func TestBind_InnerJoinSelect(t *testing.T) {
t.Parallel()
testResults := []*struct {
Happy struct {
ID int
} `boil:"h,bind"`
Fun struct {
ID int
} `boil:",bind"`
}{}
query := &Query{
selectCols: []string{"fun.id", "h.id"},
from: []string{"fun"},
joins: []join{{kind: JoinInner, clause: "happy as h on fun.happy_id = h.id"}},
}
db, mock, err := sqlmock.New()
if err != nil {
t.Error(err)
}
ret := sqlmock.NewRows([]string{"fun.id", "h.id"})
ret.AddRow(driver.Value(int64(10)), driver.Value(int64(11)))
ret.AddRow(driver.Value(int64(12)), driver.Value(int64(13)))
mock.ExpectQuery(`SELECT "fun"."id" as "fun.id", "h"."id" as "h.id" FROM "fun" INNER JOIN happy as h on fun.happy_id = h.id;`).WillReturnRows(ret)
SetExecutor(query, db)
err = query.Bind(&testResults)
if err != nil {
t.Error(err)
}
if len(testResults) != 2 {
t.Fatal("wrong number of results:", len(testResults))
}
if id := testResults[0].Happy.ID; id != 11 {
t.Error("wrong ID:", id)
}
if id := testResults[0].Fun.ID; id != 10 {
t.Error("wrong ID:", id)
}
if id := testResults[1].Happy.ID; id != 13 {
t.Error("wrong ID:", id)
}
if id := testResults[1].Fun.ID; id != 12 {
t.Error("wrong ID:", id)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Error(err)
}
}
func TestBindPtrs_Easy(t *testing.T) {
t.Parallel()
testStruct := struct {
ID int `boil:"identifier"`
Date time.Time
}{}
cols := []string{"identifier", "date"}
ptrs, err := bindPtrs(&testStruct, nil, cols...)
if err != nil {
t.Error(err)
}
if ptrs[0].(*int) != &testStruct.ID {
t.Error("id is the wrong pointer")
}
if ptrs[1].(*time.Time) != &testStruct.Date {
t.Error("id is the wrong pointer")
}
}
func TestBindPtrs_Recursive(t *testing.T) {
t.Parallel()
testStruct := struct {
Happy struct {
ID int `boil:"identifier"`
}
Fun struct {
ID int
} `boil:",bind"`
}{}
cols := []string{"id", "fun.id"}
ptrs, err := bindPtrs(&testStruct, nil, cols...)
if err != nil {
t.Error(err)
}
if ptrs[0].(*int) != &testStruct.Fun.ID {
t.Error("id is the wrong pointer")
}
if ptrs[1].(*int) != &testStruct.Fun.ID {
t.Error("id is the wrong pointer")
}
}
func TestBindPtrs_RecursiveTags(t *testing.T) {
t.Parallel()
testStruct := struct {
Happy struct {
ID int `boil:"identifier"`
} `boil:",bind"`
Fun struct {
ID int `boil:"identification"`
} `boil:",bind"`
}{}
cols := []string{"happy.identifier", "fun.identification"}
ptrs, err := bindPtrs(&testStruct, nil, cols...)
if err != nil {
t.Error(err)
}
if ptrs[0].(*int) != &testStruct.Happy.ID {
t.Error("id is the wrong pointer")
}
if ptrs[1].(*int) != &testStruct.Fun.ID {
t.Error("id is the wrong pointer")
}
}
func TestBindPtrs_Ignore(t *testing.T) {
t.Parallel()
testStruct := struct {
ID int `boil:"-"`
Happy struct {
ID int
} `boil:",bind"`
}{}
cols := []string{"id"}
ptrs, err := bindPtrs(&testStruct, nil, cols...)
if err != nil {
t.Error(err)
}
if ptrs[0].(*int) != &testStruct.Happy.ID {
t.Error("id is the wrong pointer")
}
}
func TestGetStructValues(t *testing.T) {
t.Parallel()
timeThing := time.Now()
o := struct {
TitleThing string
Name string
ID int
Stuff int
Things int
Time time.Time
NullBool null.Bool
}{
TitleThing: "patrick",
Stuff: 10,
Things: 0,
Time: timeThing,
NullBool: null.NewBool(true, false),
}
vals := GetStructValues(&o, nil, "title_thing", "name", "id", "stuff", "things", "time", "null_bool")
if vals[0].(string) != "patrick" {
t.Errorf("Want test, got %s", vals[0])
}
if vals[1].(string) != "" {
t.Errorf("Want empty string, got %s", vals[1])
}
if vals[2].(int) != 0 {
t.Errorf("Want 0, got %d", vals[2])
}
if vals[3].(int) != 10 {
t.Errorf("Want 10, got %d", vals[3])
}
if vals[4].(int) != 0 {
t.Errorf("Want 0, got %d", vals[4])
}
if !vals[5].(time.Time).Equal(timeThing) {
t.Errorf("Want %s, got %s", o.Time, vals[5])
}
if !vals[6].(null.Bool).IsZero() {
t.Errorf("Want %v, got %v", o.NullBool, vals[6])
}
}
func TestGetSliceValues(t *testing.T) {
t.Parallel()
o := []struct {
ID int
Name string
}{
{5, "a"},
{6, "b"},
}
in := make([]interface{}, len(o))
in[0] = o[0]
in[1] = o[1]
vals := GetSliceValues(in, nil, "id", "name")
if got := vals[0].(int); got != 5 {
t.Error(got)
}
if got := vals[1].(string); got != "a" {
t.Error(got)
}
if got := vals[2].(int); got != 6 {
t.Error(got)
}
if got := vals[3].(string); got != "b" {
t.Error(got)
}
}
func TestGetStructPointers(t *testing.T) {
t.Parallel()
o := struct {
Title string
ID *int
}{
Title: "patrick",
}
ptrs := GetStructPointers(&o, nil, "title", "id")
*ptrs[0].(*string) = "test"
if o.Title != "test" {
t.Errorf("Expected test, got %s", o.Title)
}
x := 5
*ptrs[1].(**int) = &x
if *o.ID != 5 {
t.Errorf("Expected 5, got %d", *o.ID)
}
}