codegen: make test fixture factory function intelligent about what fields and values it assigns
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strings"
|
||||
|
||||
"github.com/jinzhu/inflection"
|
||||
|
||||
@@ -11,6 +12,80 @@ import (
|
||||
"git.offline-twitter.com/offline-labs/gas-stack/pkg/textutils"
|
||||
)
|
||||
|
||||
// SampleValue returns a deterministic value of the appropriate type for the given column.
|
||||
func SampleValue(c pkgschema.Column, offset int) ast.Expr {
|
||||
switch c.Type {
|
||||
case "integer", "int":
|
||||
if strings.HasPrefix(c.Name, "is_") || strings.HasPrefix(c.Name, "has_") {
|
||||
// Boolean case
|
||||
if offset%2 == 0 {
|
||||
return ast.NewIdent("false")
|
||||
}
|
||||
return ast.NewIdent("true")
|
||||
} else if strings.HasSuffix(c.Name, "_at") {
|
||||
// Timestamp case
|
||||
return &ast.CallExpr{
|
||||
Fun: ast.NewIdent("TimestampFromUnix"),
|
||||
Args: []ast.Expr{
|
||||
&ast.BasicLit{Kind: token.INT, Value: fmt.Sprintf("%d", 10000+offset)},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// Regular integer case
|
||||
return &ast.BasicLit{Kind: token.INT, Value: fmt.Sprintf("%d", 10+offset)}
|
||||
}
|
||||
case "text":
|
||||
if offset == 0 {
|
||||
return &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", "asdf")}
|
||||
}
|
||||
return &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", fmt.Sprintf("asdf%d", offset))}
|
||||
case "real":
|
||||
return &ast.BasicLit{Kind: token.FLOAT, Value: fmt.Sprintf("%.2f", 1.23+float64(offset))}
|
||||
case "blob":
|
||||
return &ast.CompositeLit{
|
||||
Type: &ast.ArrayType{
|
||||
Elt: ast.NewIdent("byte"),
|
||||
},
|
||||
Elts: func() []ast.Expr {
|
||||
ret := []ast.Expr{
|
||||
&ast.BasicLit{Kind: token.INT, Value: "72"}, // 'H'
|
||||
&ast.BasicLit{Kind: token.INT, Value: "105"}, // 'i'
|
||||
&ast.BasicLit{Kind: token.INT, Value: "33"}, // '!'
|
||||
}
|
||||
for range offset {
|
||||
ret = append(ret,
|
||||
&ast.BasicLit{Kind: token.INT, Value: "33"}, // '!'
|
||||
)
|
||||
}
|
||||
return ret
|
||||
}(),
|
||||
}
|
||||
default:
|
||||
panic("Unrecognized sqlite column type: " + c.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateTestFields returns the columns for which the test generator should assign its own
|
||||
// sample values, in the MakeXyz() factory and in the create/update test: the same columns
|
||||
// that GenerateSaveItemFunc's "update" branch writes to, minus foreign keys (which can't be
|
||||
// given plausible values here) and the auto-managed "created_at"/"updated_at" columns.
|
||||
func UpdateTestFields(tbl pkgschema.Table) (ret []pkgschema.Column) {
|
||||
hasCreatedAt, hasUpdatedAt := tbl.HasAutoTimestamps()
|
||||
for _, c := range tbl.Columns {
|
||||
if c.Name == "rowid" || c.IsPrimaryKey || c.IsForeignKey {
|
||||
continue
|
||||
}
|
||||
if c.Name == "created_at" && hasCreatedAt {
|
||||
continue
|
||||
}
|
||||
if c.Name == "updated_at" && hasUpdatedAt {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, c)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GenerateModelTestAST produces an AST for a starter test file for a given model.
|
||||
func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodName string) *ast.File {
|
||||
packageName := "db"
|
||||
@@ -18,6 +93,9 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
||||
|
||||
makeHelperName := ast.NewIdent("Make" + tbl.GoTypeName)
|
||||
|
||||
hasCreatedAt, hasUpdatedAt := tbl.HasAutoTimestamps()
|
||||
updateTestFields := UpdateTestFields(tbl)
|
||||
|
||||
// func MakeItem() Item { return Item{} }
|
||||
makeItemFunc := &ast.FuncDecl{
|
||||
Name: makeHelperName,
|
||||
@@ -35,24 +113,15 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
||||
Results: []ast.Expr{
|
||||
&ast.CompositeLit{
|
||||
Type: ast.NewIdent(tbl.GoTypeName),
|
||||
Elts: []ast.Expr{
|
||||
&ast.KeyValueExpr{
|
||||
Key: ast.NewIdent("Data"),
|
||||
Value: &ast.CompositeLit{
|
||||
Type: &ast.ArrayType{
|
||||
Elt: ast.NewIdent("byte"),
|
||||
},
|
||||
Elts: []ast.Expr{},
|
||||
},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: ast.NewIdent("Description"),
|
||||
Value: &ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: `""`,
|
||||
},
|
||||
},
|
||||
},
|
||||
Elts: func() (ret []ast.Expr) {
|
||||
for _, c := range updateTestFields {
|
||||
ret = append(ret, &ast.KeyValueExpr{
|
||||
Key: ast.NewIdent(c.GoFieldName()),
|
||||
Value: SampleValue(c, 0),
|
||||
})
|
||||
}
|
||||
return
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -62,13 +131,8 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
||||
|
||||
testObj := ast.NewIdent(textutils.CamelToPascal(tbl.GoTypeName))
|
||||
testObj2 := ast.NewIdent(textutils.CamelToPascal(tbl.GoTypeName) + "2")
|
||||
fieldName := ast.NewIdent("Description") // TODO
|
||||
description1 := "an item"
|
||||
description2 := "a big item"
|
||||
testDB := ast.NewIdent("TestDB")
|
||||
|
||||
hasCreatedAt, hasUpdatedAt := tbl.HasAutoTimestamps()
|
||||
|
||||
makeDeepEqual := func(obj1 *ast.Ident, obj2 *ast.Ident) *ast.IfStmt {
|
||||
return &ast.IfStmt{
|
||||
Init: &ast.AssignStmt{
|
||||
@@ -236,23 +300,22 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{&ast.CallExpr{Fun: makeHelperName, Args: nil}},
|
||||
},
|
||||
// item.Description = "an item"
|
||||
&ast.AssignStmt{
|
||||
// item.Description = <sample value, offset 1>
|
||||
// ...one assignment per updatable field
|
||||
}
|
||||
for _, c := range updateTestFields {
|
||||
stmts = append(stmts, &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{
|
||||
&ast.SelectorExpr{
|
||||
X: testObj,
|
||||
Sel: ast.NewIdent("Description"),
|
||||
Sel: ast.NewIdent(c.GoFieldName()),
|
||||
},
|
||||
},
|
||||
Tok: token.ASSIGN,
|
||||
Rhs: []ast.Expr{
|
||||
&ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: fmt.Sprintf("%q", description1),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Rhs: []ast.Expr{SampleValue(c, 1)},
|
||||
})
|
||||
}
|
||||
stmts = append(stmts,
|
||||
// TestDB.SaveItem(&item), possibly with error check
|
||||
&ast.ExprStmt{X: func() *ast.CallExpr {
|
||||
mainExpr := &ast.CallExpr{
|
||||
@@ -274,7 +337,7 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
||||
Fun: &ast.SelectorExpr{X: ast.NewIdent("require"), Sel: ast.NewIdent("NotZero")},
|
||||
Args: []ast.Expr{ast.NewIdent("t"), &ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
|
||||
}},
|
||||
}
|
||||
)
|
||||
|
||||
// After create: assert timestamps are set
|
||||
if hasCreatedAt {
|
||||
@@ -305,14 +368,19 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
||||
stmts = append(stmts,
|
||||
BlankLine(),
|
||||
Comment("Update"),
|
||||
)
|
||||
|
||||
// item.Description = "a big item"
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: fieldName}},
|
||||
// item.Description = <sample value, offset 2>
|
||||
// ...one assignment per updatable field
|
||||
for _, c := range updateTestFields {
|
||||
stmts = append(stmts, &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent(c.GoFieldName())}},
|
||||
Tok: token.ASSIGN,
|
||||
Rhs: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", description2)}},
|
||||
},
|
||||
Rhs: []ast.Expr{SampleValue(c, 2)},
|
||||
})
|
||||
}
|
||||
|
||||
stmts = append(stmts,
|
||||
// TestDB.SaveItem(&item)
|
||||
&ast.ExprStmt{X: &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{X: testDB, Sel: ast.NewIdent("Save" + tbl.GoTypeName)},
|
||||
|
||||
Reference in New Issue
Block a user