Add more tests to insert template tests

* Add helpers to assist with template tests
* Change null []byte from string to []byte
This commit is contained in:
Patrick O'brien 2016-07-06 16:02:35 +10:00
parent d154617e95
commit 7a0b84848f
14 changed files with 309 additions and 14 deletions

View file

@ -1,5 +1,10 @@
package bdb package bdb
import (
"regexp"
"strings"
)
// Column holds information about a database column. // Column holds information about a database column.
// Types are Go types, converted by TranslateColumnType. // Types are Go types, converted by TranslateColumnType.
type Column struct { type Column struct {
@ -32,15 +37,105 @@ func FilterColumnsByDefault(defaults bool, columns []Column) []Column {
return cols return cols
} }
// FilterColumnsByAutoIncrement generates the list of auto increment columns // FilterColumnsBySimpleDefault generates a list of columns that have simple default values
func FilterColumnsByAutoIncrement(columns []Column) []Column { // A simple default value is one without a function call
func FilterColumnsBySimpleDefault(columns []Column) []Column {
var cols []Column var cols []Column
for _, c := range columns { for _, c := range columns {
if rgxAutoIncColumn.MatchString(c.Default) { if len(c.Default) != 0 && !strings.ContainsAny(c.Default, "()") {
cols = append(cols, c) cols = append(cols, c)
} }
} }
return cols return cols
} }
// FilterColumnsByAutoIncrement generates the list of auto increment columns
func FilterColumnsByAutoIncrement(autos bool, columns []Column) []Column {
var cols []Column
for _, c := range columns {
if (autos && rgxAutoIncColumn.MatchString(c.Default)) ||
(!autos && !rgxAutoIncColumn.MatchString(c.Default)) {
cols = append(cols, c)
}
}
return cols
}
var (
rgxRawDefaultValue = regexp.MustCompile(`'(.*)'::`)
rgxBoolDefaultValue = regexp.MustCompile(`(?i)true|false`)
rgxByteaDefaultValue = regexp.MustCompile(`(?i)\\x([0-9A-F]*)`)
)
// DefaultValue returns the Go converted value of the default value column
func DefaultValue(column Column) string {
defaultVal := ""
// Attempt to strip out the raw default value if its contained
// within a Postgres type cast statement
m := rgxRawDefaultValue.FindStringSubmatch(column.Default)
if len(m) > 1 {
defaultVal = m[len(m)-1]
} else {
defaultVal = column.Default
}
switch column.Type {
case "null.Uint", "null.Uint8", "null.Uint16", "null.Uint32", "null.Uint64",
"null.Int", "null.Int8", "null.Int16", "null.Int32", "null.Int64",
"uint", "uint8", "uint16", "uint32", "uint64",
"int", "int8", "int16", "int32", "int64",
"null.Float32", "null.Float64", "float32", "float64":
return defaultVal
case "null.Bool", "bool":
m = rgxBoolDefaultValue.FindStringSubmatch(defaultVal)
if len(m) == 0 {
return "false"
}
return strings.ToLower(m[0])
case "null.Time", "time.Time", "null.String", "string":
return `"` + defaultVal + `"`
case "[]byte":
m := rgxByteaDefaultValue.FindStringSubmatch(defaultVal)
if len(m) != 2 {
return `[]byte{}`
}
hexstr := m[1]
bs := make([]string, len(hexstr)/2)
c := 0
for i := 0; i < len(hexstr); i += 2 {
bs[c] = "0x" + hexstr[i:i+2]
c++
}
return `[]byte{` + strings.Join(bs, ", ") + `}`
default:
return ""
}
}
// ZeroValue returns the zero value string of the column type
func ZeroValue(column Column) string {
switch column.Type {
case "null.Uint", "null.Uint8", "null.Uint16", "null.Uint32", "null.Uint64",
"null.Int", "null.Int8", "null.Int16", "null.Int32", "null.Int64",
"uint", "uint8", "uint16", "uint32", "uint64",
"int", "int8", "int16", "int32", "int64":
return `0`
case "null.Float32", "null.Float64", "float32", "float64":
return `0.0`
case "null.String", "string":
return `""`
case "null.Bool", "bool":
return `false`
case "null.Time", "time.Time":
return `time.Time{}`
case "[]byte":
return `[]byte{}`
default:
return ""
}
}

View file

@ -52,6 +52,25 @@ func TestFilterColumnsByDefault(t *testing.T) {
} }
} }
func TestDefaultValue(t *testing.T) {
c := Column{}
c.Default = `\x12345678`
c.Type = "[]byte"
res := DefaultValue(c)
if res != `[]byte{0x12, 0x34, 0x56, 0x78}` {
t.Errorf("Invalid result: %#v", res)
}
c.Default = `\x`
res = DefaultValue(c)
if res != `[]byte{}` {
t.Errorf("Invalid result: %#v", res)
}
}
func TestFilterColumnsByAutoIncrement(t *testing.T) { func TestFilterColumnsByAutoIncrement(t *testing.T) {
t.Parallel() t.Parallel()
@ -62,7 +81,7 @@ func TestFilterColumnsByAutoIncrement(t *testing.T) {
{Name: "col4", Default: `nextval("thing"::thing)`}, {Name: "col4", Default: `nextval("thing"::thing)`},
} }
res := FilterColumnsByAutoIncrement(cols) res := FilterColumnsByAutoIncrement(true, cols)
if res[0].Name != `col1` { if res[0].Name != `col1` {
t.Errorf("Invalid result: %#v", res) t.Errorf("Invalid result: %#v", res)
} }
@ -70,7 +89,15 @@ func TestFilterColumnsByAutoIncrement(t *testing.T) {
t.Errorf("Invalid result: %#v", res) t.Errorf("Invalid result: %#v", res)
} }
res = FilterColumnsByAutoIncrement([]Column{}) res = FilterColumnsByAutoIncrement(false, cols)
if res[0].Name != `col2` {
t.Errorf("Invalid result: %#v", res)
}
if res[1].Name != `col3` {
t.Errorf("Invalid result: %#v", res)
}
res = FilterColumnsByAutoIncrement(true, []Column{})
if res != nil { if res != nil {
t.Errorf("Invalid result: %#v", res) t.Errorf("Invalid result: %#v", res)
} }

View file

@ -224,6 +224,8 @@ func (p *PostgresDriver) TranslateColumnType(c bdb.Column) bdb.Column {
c.Type = "null.Float32" c.Type = "null.Float32"
case "bit", "bit varying", "character", "character varying", "cidr", "inet", "json", "macaddr", "text", "uuid", "xml": case "bit", "bit varying", "character", "character varying", "cidr", "inet", "json", "macaddr", "text", "uuid", "xml":
c.Type = "null.String" c.Type = "null.String"
case "bytea":
c.Type = "[]byte"
case "boolean": case "boolean":
c.Type = "null.Bool" c.Type = "null.Bool"
case "date", "interval", "time", "timestamp without time zone", "timestamp with time zone": case "date", "interval", "time", "timestamp without time zone", "timestamp with time zone":

View file

@ -320,7 +320,7 @@ func randomizeField(field reflect.Value) error {
} }
case typeNullString: case typeNullString:
if b { if b {
newVal = null.NewString(randStr(5+rand.Intn(25)), b) newVal = null.NewString(randStr(1), b)
} else { } else {
newVal = null.NewString("", false) newVal = null.NewString("", false)
} }
@ -438,7 +438,7 @@ func randomizeField(field reflect.Value) error {
} }
newVal = b newVal = b
case reflect.String: case reflect.String:
newVal = randStr(5 + rand.Intn(20)) newVal = randStr(1)
case reflect.Slice: case reflect.Slice:
sliceVal := typ.Elem() sliceVal := typ.Elem()
if sliceVal.Kind() != reflect.Uint8 { if sliceVal.Kind() != reflect.Uint8 {

View file

@ -167,6 +167,7 @@ var defaultTestTemplateImports = imports{
standard: importList{ standard: importList{
`"testing"`, `"testing"`,
`"reflect"`, `"reflect"`,
`"time"`,
}, },
thirdParty: importList{ thirdParty: importList{
`"github.com/nullbio/sqlboiler/boil"`, `"github.com/nullbio/sqlboiler/boil"`,
@ -192,6 +193,7 @@ var defaultSingletonTestTemplateImports = map[string]imports{
`"os"`, `"os"`,
`"strconv"`, `"strconv"`,
`"math/rand"`, `"math/rand"`,
`"regexp"`,
`"bytes"`, `"bytes"`,
}, },
thirdParty: importList{}, thirdParty: importList{},

View file

@ -105,11 +105,12 @@ var templateStringMappers = map[string]func(string) string{
// add a function pointer here. // add a function pointer here.
var templateFunctions = template.FuncMap{ var templateFunctions = template.FuncMap{
// String ops // String ops
"substring": strmangle.Substring, "substring": strmangle.Substring,
"remove": func(rem, str string) string { return strings.Replace(str, rem, "", -1) }, "trimPrefix": func(pre, str string) string { return strings.TrimPrefix(str, pre) },
"replace": func(rep, with, str string) string { return strings.Replace(str, rep, with, -1) }, "remove": func(rem, str string) string { return strings.Replace(str, rem, "", -1) },
"prefix": func(add, str string) string { return fmt.Sprintf("%s%s", add, str) }, "replace": func(rep, with, str string) string { return strings.Replace(str, rep, with, -1) },
"quoteWrap": func(a string) string { return fmt.Sprintf(`"%s"`, a) }, "prefix": func(add, str string) string { return fmt.Sprintf("%s%s", add, str) },
"quoteWrap": func(a string) string { return fmt.Sprintf(`"%s"`, a) },
// Pluralization // Pluralization
"singular": strmangle.Singular, "singular": strmangle.Singular,
@ -135,10 +136,13 @@ var templateFunctions = template.FuncMap{
"driverUsesLastInsertID": strmangle.DriverUsesLastInsertID, "driverUsesLastInsertID": strmangle.DriverUsesLastInsertID,
"makeDBName": strmangle.MakeDBName, "makeDBName": strmangle.MakeDBName,
"filterColumnsByDefault": bdb.FilterColumnsByDefault, "filterColumnsByDefault": bdb.FilterColumnsByDefault,
"filterColumnsBySimpleDefault": bdb.FilterColumnsBySimpleDefault,
"filterColumnsByAutoIncrement": bdb.FilterColumnsByAutoIncrement, "filterColumnsByAutoIncrement": bdb.FilterColumnsByAutoIncrement,
"autoIncPrimaryKey": bdb.AutoIncPrimaryKey, "autoIncPrimaryKey": bdb.AutoIncPrimaryKey,
"sqlColDefinitions": bdb.SQLColDefinitions, "sqlColDefinitions": bdb.SQLColDefinitions,
"sqlColDefStrings": bdb.SQLColDefStrings, "sqlColDefStrings": bdb.SQLColDefStrings,
"columnNames": bdb.ColumnNames, "columnNames": bdb.ColumnNames,
"toManyRelationships": bdb.ToManyRelationships, "toManyRelationships": bdb.ToManyRelationships,
"zeroValue": bdb.ZeroValue,
"defaultValue": bdb.DefaultValue,
} }

View file

@ -5,7 +5,7 @@ var (
{{$varNameSingular}}ColumnsWithoutDefault = []string{{"{"}}{{.Table.Columns | filterColumnsByDefault false | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} {{$varNameSingular}}ColumnsWithoutDefault = []string{{"{"}}{{.Table.Columns | filterColumnsByDefault false | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}}
{{$varNameSingular}}ColumnsWithDefault = []string{{"{"}}{{.Table.Columns | filterColumnsByDefault true | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}} {{$varNameSingular}}ColumnsWithDefault = []string{{"{"}}{{.Table.Columns | filterColumnsByDefault true | columnNames | stringMap .StringFuncs.quoteWrap | join ","}}{{"}"}}
{{$varNameSingular}}PrimaryKeyColumns = []string{{"{"}}{{.Table.PKey.Columns | stringMap .StringFuncs.quoteWrap | join ", "}}{{"}"}} {{$varNameSingular}}PrimaryKeyColumns = []string{{"{"}}{{.Table.PKey.Columns | stringMap .StringFuncs.quoteWrap | join ", "}}{{"}"}}
{{$varNameSingular}}AutoIncrementColumns = []string{{"{"}}{{.Table.Columns | filterColumnsByAutoIncrement | columnNames | stringMap .StringFuncs.quoteWrap | join "," }}{{"}"}} {{$varNameSingular}}AutoIncrementColumns = []string{{"{"}}{{.Table.Columns | filterColumnsByAutoIncrement true | columnNames | stringMap .StringFuncs.quoteWrap | join "," }}{{"}"}}
{{$varNameSingular}}AutoIncPrimaryKey = "{{- with autoIncPrimaryKey .Table.Columns .Table.PKey -}}{{.Name}}{{- end -}}" {{$varNameSingular}}AutoIncPrimaryKey = "{{- with autoIncPrimaryKey .Table.Columns .Table.PKey -}}{{.Name}}{{- end -}}"
) )

View file

@ -0,0 +1,27 @@
{{- $tableNameSingular := .Table.Name | singular | titleCase -}}
{{- $dbName := singular .Table.Name -}}
{{- $tableNamePlural := .Table.Name | plural | titleCase -}}
{{- $varNamePlural := .Table.Name | plural | camelCase -}}
{{- $varNameSingular := .Table.Name | singular | camelCase -}}
func {{$varNameSingular}}BeforeCreateHook(o *{{$tableNameSingular}}) error {
return nil
}
func {{$varNameSingular}}AfterCreateHook(o *{{$tableNameSingular}}) error {
return nil
}
func {{$varNameSingular}}BeforeUpdateHook(o *{{$tableNameSingular}}) error {
return nil
}
func {{$varNameSingular}}AfterUpdateHook(o *{{$tableNameSingular}}) error {
return nil
}
func Test{{$tableNamePlural}}Hooks(t *testing.T) {
// var err error
{{$varNamePlural}}DeleteAllRows(t)
t.Errorf("Hook tests not implemented")
}

View file

@ -0,0 +1,109 @@
{{- $tableNameSingular := .Table.Name | singular | titleCase -}}
{{- $dbName := singular .Table.Name -}}
{{- $tableNamePlural := .Table.Name | plural | titleCase -}}
{{- $varNamePlural := .Table.Name | plural | camelCase -}}
{{- $varNameSingular := .Table.Name | singular | camelCase -}}
func Test{{$tableNamePlural}}Insert(t *testing.T) {
var err error
{{$varNamePlural}}DeleteAllRows(t)
o := make({{$varNameSingular}}Slice, 3)
if err = boil.RandomizeSlice(&o); err != nil {
t.Errorf("Unable to randomize {{$tableNameSingular}} slice: %s", err)
}
for i := 0; i < len(o); i++ {
if err = o[i].Insert(); err != nil {
t.Errorf("Unable to insert {{$tableNameSingular}}:\n%#v\nErr: %s", o[i], err)
}
}
j := make({{$varNameSingular}}Slice, 3)
// Perform all Find queries and assign result objects to slice for comparison
for i := 0; i < len(j); i++ {
j[i], err = {{$tableNameSingular}}Find({{.Table.PKey.Columns | stringMap .StringFuncs.titleCase | prefixStringSlice "o[i]." | join ", "}})
{{$varNameSingular}}CompareVals(o[i], j[i], t)
}
// Ensure we can store zero-valued object successfully.
item := &{{$tableNameSingular}}{}
if err = item.Insert(); err != nil {
t.Errorf("Unable to insert zero-value item {{$tableNameSingular}}:\n%#v\nErr: %s", item, err)
}
{{with .Table.Columns | filterColumnsByAutoIncrement true | columnNames}}
// Ensure the auto increment columns are returned in the object
{{range .}}
if item.{{titleCase .}} <= 0 {
t.Errorf("Expected the auto-increment primary key to be greater than 0, got: %d", item.{{titleCase .}})
}
{{end}}
{{end}}
emptyTime := time.Time{}.String()
{{with .Table.Columns | filterColumnsBySimpleDefault}}
// Ensure the default value columns are returned in the object
{{range .}}
{{$tc := titleCase .Name}}
{{$zv := zeroValue .}}
{{$dv := defaultValue .}}
{{$ty := trimPrefix "null." .Type}}
{{if and (ne $ty "[]byte") .IsNullable}}
if item.{{$tc}}.Valid == false {
t.Errorf("Expected the nullable default value column {{$tc}} of {{$tableNameSingular}} to be valid.")
}
{{if eq .Type "null.Time"}}
if (item.{{$tc}}.{{$ty}}.String() == emptyTime && !isZeroTime({{$dv}})) ||
(item.{{$tc}}.{{$ty}}.String() > emptyTime && isZeroTime({{$dv}})) {
{{else}}
if item.{{$tc}}.{{$ty}} != {{$dv}} {
{{- end -}}
t.Errorf("Expected the nullable default value column {{$tc}} of {{$tableNameSingular}} to match the database default value:\n%#v\n%v\n\n", item.{{$tc}}.{{$ty}}, {{$dv}})
}
{{else}}
{{if eq .Type "[]byte"}}
if string(item.{{$tc}}) != string({{$dv}}) {
{{else if eq .Type "time.Time"}}
if (item.{{$tc}}.String() == emptyTime && !isZeroTime({{$dv}})) ||
(item.{{$tc}}.String() > emptyTime && isZeroTime({{$dv}})) {
{{else}}
if item.{{$tc}} != {{$dv}} {
{{- end -}}
t.Errorf("Expected the default value column {{$tc}} of {{$tableNameSingular}} to match the database default value:\n%#v\n%v\n\n", item.{{$tc}}, {{$dv}})
}
{{end}}
{{end}}
{{end}}
{{with .Table.Columns | filterColumnsByAutoIncrement false | filterColumnsByDefault false}}
// Ensure the non-defaultvalue columns and non-autoincrement columns are stored correctly as zero or null values.
{{range .}}
{{- $tc := titleCase .Name -}}
{{- $zv := zeroValue . -}}
{{$ty := trimPrefix "null." .Type}}
{{if and (ne $ty "[]byte") .IsNullable}}
if item.{{$tc}}.Valid == true {
t.Errorf("Expected the nullable column {{$tc}} of {{$tableNameSingular}} to be invalid (null).")
}
{{if eq .Type "null.Time"}}
if item.{{$tc}}.{{$ty}}.String() != emptyTime {
{{else}}
if item.{{$tc}}.{{$ty}} != {{$zv}} {
{{- end -}}
t.Errorf("Expected the nullable column {{$tc}} of {{$tableNameSingular}} to be a zero-value (null):\n%#v\n%v\n\n", item.{{$tc}}.{{$ty}}, {{$zv}})
}
{{else}}
{{if eq .Type "[]byte"}}
if string(item.{{$tc}}) != string({{$zv}}) {
{{else if eq .Type "time.Time"}}
if item.{{$tc}}.String() != emptyTime {
{{else}}
if item.{{$tc}} != {{$zv}} {
{{- end -}}
t.Errorf("Expected the column {{$tc}} of {{$tableNameSingular}} to be a zero-value (null):\n%#v\n%v\n\n", item.{{$tc}}, {{$zv}})
}
{{- end}}
{{end}}
{{end}}
}

View file

@ -14,7 +14,7 @@ func TestMain(m *testing.M) {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
// Set DebugMode so we can see generated sql statements // Set DebugMode so we can see generated sql statements
boil.DebugMode = true boil.DebugMode = false
var err error var err error
if err = setup(); err != nil { if err = setup(); err != nil {

View file

@ -0,0 +1,8 @@
{{- $tableNameSingular := .Table.Name | singular | titleCase -}}
{{- $dbName := singular .Table.Name -}}
{{- $tableNamePlural := .Table.Name | plural | titleCase -}}
{{- $varNamePlural := .Table.Name | plural | camelCase -}}
{{- $varNameSingular := .Table.Name | singular | camelCase -}}
func Test{{$tableNamePlural}}Select(t *testing.T) {
t.Errorf("test select not implemented")
}

View file

@ -1,5 +1,10 @@
var dbNameRand *rand.Rand var dbNameRand *rand.Rand
func isZeroTime(time string) bool {
re := regexp.MustCompile(`[2-9]+`)
return !re.MatchString(time)
}
func initDBNameRand(input string) { func initDBNameRand(input string) {
sum := md5.Sum([]byte(input)) sum := md5.Sum([]byte(input))

View file

@ -0,0 +1,8 @@
{{- $tableNameSingular := .Table.Name | singular | titleCase -}}
{{- $dbName := singular .Table.Name -}}
{{- $tableNamePlural := .Table.Name | plural | titleCase -}}
{{- $varNamePlural := .Table.Name | plural | camelCase -}}
{{- $varNameSingular := .Table.Name | singular | camelCase -}}
func Test{{$tableNamePlural}}Struct(t *testing.T) {
t.Errorf("test struct not implemented")
}

View file

@ -0,0 +1,8 @@
{{- $tableNameSingular := .Table.Name | singular | titleCase -}}
{{- $dbName := singular .Table.Name -}}
{{- $tableNamePlural := .Table.Name | plural | titleCase -}}
{{- $varNamePlural := .Table.Name | plural | camelCase -}}
{{- $varNameSingular := .Table.Name | singular | camelCase -}}
func Test{{$tableNamePlural}}Update(t *testing.T) {
t.Errorf("test update not implemented")
}