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"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/jinzhu/inflection"
|
"github.com/jinzhu/inflection"
|
||||||
|
|
||||||
@@ -11,6 +12,80 @@ import (
|
|||||||
"git.offline-twitter.com/offline-labs/gas-stack/pkg/textutils"
|
"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.
|
// 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 {
|
func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodName string) *ast.File {
|
||||||
packageName := "db"
|
packageName := "db"
|
||||||
@@ -18,6 +93,9 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
|||||||
|
|
||||||
makeHelperName := ast.NewIdent("Make" + tbl.GoTypeName)
|
makeHelperName := ast.NewIdent("Make" + tbl.GoTypeName)
|
||||||
|
|
||||||
|
hasCreatedAt, hasUpdatedAt := tbl.HasAutoTimestamps()
|
||||||
|
updateTestFields := UpdateTestFields(tbl)
|
||||||
|
|
||||||
// func MakeItem() Item { return Item{} }
|
// func MakeItem() Item { return Item{} }
|
||||||
makeItemFunc := &ast.FuncDecl{
|
makeItemFunc := &ast.FuncDecl{
|
||||||
Name: makeHelperName,
|
Name: makeHelperName,
|
||||||
@@ -35,24 +113,15 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
|||||||
Results: []ast.Expr{
|
Results: []ast.Expr{
|
||||||
&ast.CompositeLit{
|
&ast.CompositeLit{
|
||||||
Type: ast.NewIdent(tbl.GoTypeName),
|
Type: ast.NewIdent(tbl.GoTypeName),
|
||||||
Elts: []ast.Expr{
|
Elts: func() (ret []ast.Expr) {
|
||||||
&ast.KeyValueExpr{
|
for _, c := range updateTestFields {
|
||||||
Key: ast.NewIdent("Data"),
|
ret = append(ret, &ast.KeyValueExpr{
|
||||||
Value: &ast.CompositeLit{
|
Key: ast.NewIdent(c.GoFieldName()),
|
||||||
Type: &ast.ArrayType{
|
Value: SampleValue(c, 0),
|
||||||
Elt: ast.NewIdent("byte"),
|
})
|
||||||
},
|
}
|
||||||
Elts: []ast.Expr{},
|
return
|
||||||
},
|
}(),
|
||||||
},
|
|
||||||
&ast.KeyValueExpr{
|
|
||||||
Key: ast.NewIdent("Description"),
|
|
||||||
Value: &ast.BasicLit{
|
|
||||||
Kind: token.STRING,
|
|
||||||
Value: `""`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -62,13 +131,8 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
|||||||
|
|
||||||
testObj := ast.NewIdent(textutils.CamelToPascal(tbl.GoTypeName))
|
testObj := ast.NewIdent(textutils.CamelToPascal(tbl.GoTypeName))
|
||||||
testObj2 := ast.NewIdent(textutils.CamelToPascal(tbl.GoTypeName) + "2")
|
testObj2 := ast.NewIdent(textutils.CamelToPascal(tbl.GoTypeName) + "2")
|
||||||
fieldName := ast.NewIdent("Description") // TODO
|
|
||||||
description1 := "an item"
|
|
||||||
description2 := "a big item"
|
|
||||||
testDB := ast.NewIdent("TestDB")
|
testDB := ast.NewIdent("TestDB")
|
||||||
|
|
||||||
hasCreatedAt, hasUpdatedAt := tbl.HasAutoTimestamps()
|
|
||||||
|
|
||||||
makeDeepEqual := func(obj1 *ast.Ident, obj2 *ast.Ident) *ast.IfStmt {
|
makeDeepEqual := func(obj1 *ast.Ident, obj2 *ast.Ident) *ast.IfStmt {
|
||||||
return &ast.IfStmt{
|
return &ast.IfStmt{
|
||||||
Init: &ast.AssignStmt{
|
Init: &ast.AssignStmt{
|
||||||
@@ -236,23 +300,22 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
|||||||
Tok: token.DEFINE,
|
Tok: token.DEFINE,
|
||||||
Rhs: []ast.Expr{&ast.CallExpr{Fun: makeHelperName, Args: nil}},
|
Rhs: []ast.Expr{&ast.CallExpr{Fun: makeHelperName, Args: nil}},
|
||||||
},
|
},
|
||||||
// item.Description = "an item"
|
// item.Description = <sample value, offset 1>
|
||||||
&ast.AssignStmt{
|
// ...one assignment per updatable field
|
||||||
|
}
|
||||||
|
for _, c := range updateTestFields {
|
||||||
|
stmts = append(stmts, &ast.AssignStmt{
|
||||||
Lhs: []ast.Expr{
|
Lhs: []ast.Expr{
|
||||||
&ast.SelectorExpr{
|
&ast.SelectorExpr{
|
||||||
X: testObj,
|
X: testObj,
|
||||||
Sel: ast.NewIdent("Description"),
|
Sel: ast.NewIdent(c.GoFieldName()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Tok: token.ASSIGN,
|
Tok: token.ASSIGN,
|
||||||
Rhs: []ast.Expr{
|
Rhs: []ast.Expr{SampleValue(c, 1)},
|
||||||
&ast.BasicLit{
|
})
|
||||||
Kind: token.STRING,
|
}
|
||||||
Value: fmt.Sprintf("%q", description1),
|
stmts = append(stmts,
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// TestDB.SaveItem(&item), possibly with error check
|
// TestDB.SaveItem(&item), possibly with error check
|
||||||
&ast.ExprStmt{X: func() *ast.CallExpr {
|
&ast.ExprStmt{X: func() *ast.CallExpr {
|
||||||
mainExpr := &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")},
|
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")}},
|
Args: []ast.Expr{ast.NewIdent("t"), &ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
|
||||||
}},
|
}},
|
||||||
}
|
)
|
||||||
|
|
||||||
// After create: assert timestamps are set
|
// After create: assert timestamps are set
|
||||||
if hasCreatedAt {
|
if hasCreatedAt {
|
||||||
@@ -305,14 +368,19 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
|||||||
stmts = append(stmts,
|
stmts = append(stmts,
|
||||||
BlankLine(),
|
BlankLine(),
|
||||||
Comment("Update"),
|
Comment("Update"),
|
||||||
|
)
|
||||||
|
|
||||||
// item.Description = "a big item"
|
// item.Description = <sample value, offset 2>
|
||||||
&ast.AssignStmt{
|
// ...one assignment per updatable field
|
||||||
Lhs: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: fieldName}},
|
for _, c := range updateTestFields {
|
||||||
|
stmts = append(stmts, &ast.AssignStmt{
|
||||||
|
Lhs: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent(c.GoFieldName())}},
|
||||||
Tok: token.ASSIGN,
|
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)
|
// TestDB.SaveItem(&item)
|
||||||
&ast.ExprStmt{X: &ast.CallExpr{
|
&ast.ExprStmt{X: &ast.CallExpr{
|
||||||
Fun: &ast.SelectorExpr{X: testDB, Sel: ast.NewIdent("Save" + tbl.GoTypeName)},
|
Fun: &ast.SelectorExpr{X: testDB, Sel: ast.NewIdent("Save" + tbl.GoTypeName)},
|
||||||
|
|||||||
Reference in New Issue
Block a user