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:
parent
d154617e95
commit
7a0b84848f
14 changed files with 309 additions and 14 deletions
101
bdb/column.go
101
bdb/column.go
|
@ -1,5 +1,10 @@
|
|||
package bdb
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Column holds information about a database column.
|
||||
// Types are Go types, converted by TranslateColumnType.
|
||||
type Column struct {
|
||||
|
@ -32,15 +37,105 @@ func FilterColumnsByDefault(defaults bool, columns []Column) []Column {
|
|||
return cols
|
||||
}
|
||||
|
||||
// FilterColumnsByAutoIncrement generates the list of auto increment columns
|
||||
func FilterColumnsByAutoIncrement(columns []Column) []Column {
|
||||
// FilterColumnsBySimpleDefault generates a list of columns that have simple default values
|
||||
// A simple default value is one without a function call
|
||||
func FilterColumnsBySimpleDefault(columns []Column) []Column {
|
||||
var cols []Column
|
||||
|
||||
for _, c := range columns {
|
||||
if rgxAutoIncColumn.MatchString(c.Default) {
|
||||
if len(c.Default) != 0 && !strings.ContainsAny(c.Default, "()") {
|
||||
cols = append(cols, c)
|
||||
}
|
||||
}
|
||||
|
||||
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 ""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -62,7 +81,7 @@ func TestFilterColumnsByAutoIncrement(t *testing.T) {
|
|||
{Name: "col4", Default: `nextval("thing"::thing)`},
|
||||
}
|
||||
|
||||
res := FilterColumnsByAutoIncrement(cols)
|
||||
res := FilterColumnsByAutoIncrement(true, cols)
|
||||
if res[0].Name != `col1` {
|
||||
t.Errorf("Invalid result: %#v", res)
|
||||
}
|
||||
|
@ -70,7 +89,15 @@ func TestFilterColumnsByAutoIncrement(t *testing.T) {
|
|||
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 {
|
||||
t.Errorf("Invalid result: %#v", res)
|
||||
}
|
||||
|
|
|
@ -224,6 +224,8 @@ func (p *PostgresDriver) TranslateColumnType(c bdb.Column) bdb.Column {
|
|||
c.Type = "null.Float32"
|
||||
case "bit", "bit varying", "character", "character varying", "cidr", "inet", "json", "macaddr", "text", "uuid", "xml":
|
||||
c.Type = "null.String"
|
||||
case "bytea":
|
||||
c.Type = "[]byte"
|
||||
case "boolean":
|
||||
c.Type = "null.Bool"
|
||||
case "date", "interval", "time", "timestamp without time zone", "timestamp with time zone":
|
||||
|
|
|
@ -320,7 +320,7 @@ func randomizeField(field reflect.Value) error {
|
|||
}
|
||||
case typeNullString:
|
||||
if b {
|
||||
newVal = null.NewString(randStr(5+rand.Intn(25)), b)
|
||||
newVal = null.NewString(randStr(1), b)
|
||||
} else {
|
||||
newVal = null.NewString("", false)
|
||||
}
|
||||
|
@ -438,7 +438,7 @@ func randomizeField(field reflect.Value) error {
|
|||
}
|
||||
newVal = b
|
||||
case reflect.String:
|
||||
newVal = randStr(5 + rand.Intn(20))
|
||||
newVal = randStr(1)
|
||||
case reflect.Slice:
|
||||
sliceVal := typ.Elem()
|
||||
if sliceVal.Kind() != reflect.Uint8 {
|
||||
|
|
|
@ -167,6 +167,7 @@ var defaultTestTemplateImports = imports{
|
|||
standard: importList{
|
||||
`"testing"`,
|
||||
`"reflect"`,
|
||||
`"time"`,
|
||||
},
|
||||
thirdParty: importList{
|
||||
`"github.com/nullbio/sqlboiler/boil"`,
|
||||
|
@ -192,6 +193,7 @@ var defaultSingletonTestTemplateImports = map[string]imports{
|
|||
`"os"`,
|
||||
`"strconv"`,
|
||||
`"math/rand"`,
|
||||
`"regexp"`,
|
||||
`"bytes"`,
|
||||
},
|
||||
thirdParty: importList{},
|
||||
|
|
14
templates.go
14
templates.go
|
@ -105,11 +105,12 @@ var templateStringMappers = map[string]func(string) string{
|
|||
// add a function pointer here.
|
||||
var templateFunctions = template.FuncMap{
|
||||
// String ops
|
||||
"substring": strmangle.Substring,
|
||||
"remove": func(rem, str string) string { return strings.Replace(str, rem, "", -1) },
|
||||
"replace": func(rep, with, str string) string { return strings.Replace(str, rep, with, -1) },
|
||||
"prefix": func(add, str string) string { return fmt.Sprintf("%s%s", add, str) },
|
||||
"quoteWrap": func(a string) string { return fmt.Sprintf(`"%s"`, a) },
|
||||
"substring": strmangle.Substring,
|
||||
"trimPrefix": func(pre, str string) string { return strings.TrimPrefix(str, pre) },
|
||||
"remove": func(rem, str string) string { return strings.Replace(str, rem, "", -1) },
|
||||
"replace": func(rep, with, str string) string { return strings.Replace(str, rep, with, -1) },
|
||||
"prefix": func(add, str string) string { return fmt.Sprintf("%s%s", add, str) },
|
||||
"quoteWrap": func(a string) string { return fmt.Sprintf(`"%s"`, a) },
|
||||
|
||||
// Pluralization
|
||||
"singular": strmangle.Singular,
|
||||
|
@ -135,10 +136,13 @@ var templateFunctions = template.FuncMap{
|
|||
"driverUsesLastInsertID": strmangle.DriverUsesLastInsertID,
|
||||
"makeDBName": strmangle.MakeDBName,
|
||||
"filterColumnsByDefault": bdb.FilterColumnsByDefault,
|
||||
"filterColumnsBySimpleDefault": bdb.FilterColumnsBySimpleDefault,
|
||||
"filterColumnsByAutoIncrement": bdb.FilterColumnsByAutoIncrement,
|
||||
"autoIncPrimaryKey": bdb.AutoIncPrimaryKey,
|
||||
"sqlColDefinitions": bdb.SQLColDefinitions,
|
||||
"sqlColDefStrings": bdb.SQLColDefStrings,
|
||||
"columnNames": bdb.ColumnNames,
|
||||
"toManyRelationships": bdb.ToManyRelationships,
|
||||
"zeroValue": bdb.ZeroValue,
|
||||
"defaultValue": bdb.DefaultValue,
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ var (
|
|||
{{$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}}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 -}}"
|
||||
)
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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}}
|
||||
}
|
|
@ -14,7 +14,7 @@ func TestMain(m *testing.M) {
|
|||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
// Set DebugMode so we can see generated sql statements
|
||||
boil.DebugMode = true
|
||||
boil.DebugMode = false
|
||||
|
||||
var err error
|
||||
if err = setup(); err != nil {
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -1,5 +1,10 @@
|
|||
var dbNameRand *rand.Rand
|
||||
|
||||
func isZeroTime(time string) bool {
|
||||
re := regexp.MustCompile(`[2-9]+`)
|
||||
return !re.MatchString(time)
|
||||
}
|
||||
|
||||
func initDBNameRand(input string) {
|
||||
sum := md5.Sum([]byte(input))
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
Loading…
Reference in a new issue