Compare commits

...

4 Commits

Author SHA1 Message Date
dbf14e23b6 codegen: fix fk checking lambda producing non-lint-passing code
All checks were successful
CI / build-docker (push) Successful in 4s
CI / build-docker-bootstrap (push) Has been skipped
CI / release-test (push) Successful in 2m42s
2026-05-29 17:34:40 -07:00
ad1782c73d codegen: use "require.NoError(...)" when saving objects that return errors 2026-05-29 15:36:36 -07:00
ccd7e32cbf codegen: test file now uses deep.Equal instead of comparing one field 2026-05-29 14:06:09 -07:00
ed4ade1956 codegen: fix escaped double-quote inside test strings 2026-05-29 13:44:13 -07:00
2 changed files with 161 additions and 123 deletions

View File

@ -200,7 +200,7 @@ func buildFKCheckLambda(tbl schema.Table) (*ast.AssignStmt, bool) {
ret = append(ret, &ast.IfStmt{
Init: &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent("err")},
Tok: token.ASSIGN,
Tok: token.DEFINE,
Rhs: []ast.Expr{
&ast.CallExpr{
Fun: &ast.SelectorExpr{X: dbDB, Sel: ast.NewIdent("Get")},

View File

@ -63,12 +63,52 @@ 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"`
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{
Lhs: []ast.Expr{
&ast.Ident{Name: "diff"},
},
Tok: token.DEFINE,
Rhs: []ast.Expr{
&ast.CallExpr{
Fun: &ast.SelectorExpr{
X: &ast.Ident{Name: "deep"},
Sel: &ast.Ident{Name: "Equal"},
},
Args: []ast.Expr{obj1, obj2},
},
},
},
Cond: &ast.BinaryExpr{
X: &ast.Ident{Name: "diff"},
Op: token.NEQ,
Y: &ast.Ident{Name: "nil"},
},
Body: &ast.BlockStmt{
List: []ast.Stmt{
&ast.ExprStmt{
X: &ast.CallExpr{
Fun: &ast.SelectorExpr{
X: &ast.Ident{Name: "t"},
Sel: &ast.Ident{Name: "Error"},
},
Args: []ast.Expr{
&ast.Ident{Name: "diff"},
},
},
},
},
},
}
}
testFuncType := &ast.FuncType{
Params: &ast.FieldList{
List: []*ast.Field{{
@ -78,6 +118,103 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
},
}
// Generate FK Check test func first, because it also detects whether there are foreign keys
shouldIncludeTestFkCheck := false
testFkChecking := &ast.FuncDecl{
Name: ast.NewIdent("Test" + tbl.GoTypeName + "FkChecking"),
Type: testFuncType,
Body: &ast.BlockStmt{
List: func() (stmts []ast.Stmt) {
isFirst := true
for _, col := range tbl.Columns {
if !col.IsForeignKey {
continue
}
shouldIncludeTestFkCheck = true
// post := MakePost()
if !isFirst {
stmts = append(stmts, BlankLine())
}
stmts = append(stmts, []ast.Stmt{
// Comment header
Comment(fmt.Sprintf("Invalid %s", col.GoFieldName())),
// `Invalid
&ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(tbl.VarName)},
Tok: map[bool]token.Token{true: token.DEFINE, false: token.ASSIGN}[isFirst],
Rhs: []ast.Expr{
&ast.CallExpr{
Fun: ast.NewIdent("Make" + tbl.GoTypeName),
},
},
},
// `post.QuotedPostID = 94354538969386985`
&ast.AssignStmt{
Lhs: []ast.Expr{
&ast.SelectorExpr{
X: ast.NewIdent(tbl.VarName),
Sel: ast.NewIdent(col.GoFieldName()),
},
},
Tok: token.ASSIGN,
Rhs: []ast.Expr{
&ast.BasicLit{
Kind: token.INT,
Value: "94354538969386985",
},
},
},
// `err := db.SavePost(&post)`
&ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent("err")},
Tok: map[bool]token.Token{true: token.DEFINE, false: token.ASSIGN}[isFirst],
Rhs: []ast.Expr{
&ast.CallExpr{
Fun: &ast.SelectorExpr{
X: testDB,
Sel: ast.NewIdent("Save" + tbl.GoTypeName),
},
Args: []ast.Expr{
&ast.UnaryExpr{
Op: token.AND,
X: ast.NewIdent(tbl.VarName),
},
},
},
},
},
// `assertForeignKeyError(t, err, "QuotedPostID", post.QuotedPostID)`
&ast.ExprStmt{
X: &ast.CallExpr{
Fun: ast.NewIdent("AssertForeignKeyError"),
Args: []ast.Expr{
ast.NewIdent("t"),
ast.NewIdent("err"),
&ast.BasicLit{
Kind: token.STRING,
Value: fmt.Sprintf("%q", col.GoFieldName()),
},
&ast.SelectorExpr{
X: ast.NewIdent(tbl.VarName),
Sel: ast.NewIdent(col.GoFieldName()),
},
},
},
},
}...)
isFirst = false
}
return stmts
}(),
},
}
testCreateUpdateDelete := &ast.FuncDecl{
Name: ast.NewIdent("TestCreateUpdateDelete" + tbl.GoTypeName),
Type: testFuncType,
@ -116,11 +253,21 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
},
},
// TestDB.SaveItem(&item)
&ast.ExprStmt{X: &ast.CallExpr{
Fun: &ast.SelectorExpr{X: testDB, Sel: ast.NewIdent("Save" + tbl.GoTypeName)},
Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: testObj}},
}},
// TestDB.SaveItem(&item), possibly with error check
&ast.ExprStmt{X: func() *ast.CallExpr {
mainExpr := &ast.CallExpr{
Fun: &ast.SelectorExpr{X: testDB, Sel: ast.NewIdent("Save" + tbl.GoTypeName)},
Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: testObj}},
}
if shouldIncludeTestFkCheck {
// Also a check for whether the Save function returns an error
return &ast.CallExpr{
Fun: &ast.SelectorExpr{X: ast.NewIdent("require"), Sel: ast.NewIdent("NoError")},
Args: []ast.Expr{ast.NewIdent("t"), mainExpr},
}
}
return mainExpr
}()},
// require.NotZero(t, item.ID)
&ast.ExprStmt{X: &ast.CallExpr{
@ -151,15 +298,8 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
})},
},
// assert.Equal(t, item.Description, item2.Description)
&ast.ExprStmt{X: &ast.CallExpr{
Fun: &ast.SelectorExpr{X: ast.NewIdent("assert"), Sel: ast.NewIdent("Equal")},
Args: []ast.Expr{
ast.NewIdent("t"),
&ast.SelectorExpr{X: testObj, Sel: fieldName},
&ast.SelectorExpr{X: testObj2, Sel: fieldName},
},
}},
// if deep.Equal(...) {...}
makeDeepEqual(testObj, testObj2),
)
stmts = append(stmts,
@ -170,7 +310,7 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
&ast.AssignStmt{
Lhs: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: fieldName}},
Tok: token.ASSIGN,
Rhs: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: description2}},
Rhs: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", description2)}},
},
// TestDB.SaveItem(&item)
@ -189,15 +329,8 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
})},
},
// assert.Equal(t, item.Description, item2.Description)
&ast.ExprStmt{X: &ast.CallExpr{
Fun: &ast.SelectorExpr{X: ast.NewIdent("assert"), Sel: ast.NewIdent("Equal")},
Args: []ast.Expr{
ast.NewIdent("t"),
&ast.SelectorExpr{X: testObj, Sel: fieldName},
&ast.SelectorExpr{X: testObj2, Sel: fieldName},
},
}},
// if deep.Equal(...) {...}
makeDeepEqual(testObj, testObj2),
)
indexGets, hasIndexedGets := []ast.Stmt{
@ -292,102 +425,6 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
},
}
shouldIncludeTestFkCheck := false
testFkChecking := &ast.FuncDecl{
Name: ast.NewIdent("Test" + tbl.GoTypeName + "FkChecking"),
Type: testFuncType,
Body: &ast.BlockStmt{
List: func() (stmts []ast.Stmt) {
isFirst := true
for _, col := range tbl.Columns {
if !col.IsForeignKey {
continue
}
shouldIncludeTestFkCheck = true
// post := MakePost()
if !isFirst {
stmts = append(stmts, BlankLine())
}
stmts = append(stmts, []ast.Stmt{
// Comment header
Comment(fmt.Sprintf("Invalid %s", col.GoFieldName())),
// `Invalid BlahBlahID`
&ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(tbl.VarName)},
Tok: map[bool]token.Token{true: token.DEFINE, false: token.ASSIGN}[isFirst],
Rhs: []ast.Expr{
&ast.CallExpr{
Fun: ast.NewIdent("Make" + tbl.GoTypeName),
},
},
},
// `post.QuotedPostID = 94354538969386985`
&ast.AssignStmt{
Lhs: []ast.Expr{
&ast.SelectorExpr{
X: ast.NewIdent(tbl.VarName),
Sel: ast.NewIdent(col.GoFieldName()),
},
},
Tok: token.ASSIGN,
Rhs: []ast.Expr{
&ast.BasicLit{
Kind: token.INT,
Value: "94354538969386985",
},
},
},
// `err := db.SavePost(&post)`
&ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent("err")},
Tok: map[bool]token.Token{true: token.DEFINE, false: token.ASSIGN}[isFirst],
Rhs: []ast.Expr{
&ast.CallExpr{
Fun: &ast.SelectorExpr{
X: testDB,
Sel: ast.NewIdent("Save" + tbl.GoTypeName),
},
Args: []ast.Expr{
&ast.UnaryExpr{
Op: token.AND,
X: ast.NewIdent(tbl.VarName),
},
},
},
},
},
// `assertForeignKeyError(t, err, "QuotedPostID", post.QuotedPostID)`
&ast.ExprStmt{
X: &ast.CallExpr{
Fun: ast.NewIdent("AssertForeignKeyError"),
Args: []ast.Expr{
ast.NewIdent("t"),
ast.NewIdent("err"),
&ast.BasicLit{
Kind: token.STRING,
Value: fmt.Sprintf("%q", col.GoFieldName()),
},
&ast.SelectorExpr{
X: ast.NewIdent(tbl.VarName),
Sel: ast.NewIdent(col.GoFieldName()),
},
},
},
},
}...)
isFirst = false
}
return stmts
}(),
},
}
testList := []ast.Decl{
makeItemFunc,
testCreateUpdateDelete,
@ -415,6 +452,7 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
Path: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf(`"%s/pkg/%s"`, gomodName, packageName)},
Name: ast.NewIdent("."),
},
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"github.com/go-test/deep"`}},
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"github.com/stretchr/testify/assert"`}},
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"github.com/stretchr/testify/require"`}},
},