codegen: add auto-timestamp checking to generated tests

This commit is contained in:
wispem-wantex 2026-01-31 20:35:03 -08:00
parent 5973a2a4b7
commit f0c152cfe4

View File

@ -113,6 +113,8 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
description1 := `"an item"` description1 := `"an item"`
description2 := `"a big item"` description2 := `"a big item"`
hasCreatedAt, hasUpdatedAt := tbl.HasAutoTimestamps()
testCreateUpdateDelete := &ast.FuncDecl{ testCreateUpdateDelete := &ast.FuncDecl{
Name: ast.NewIdent("TestCreateUpdateDelete" + tbl.GoTypeName), Name: ast.NewIdent("TestCreateUpdateDelete" + tbl.GoTypeName),
Type: &ast.FuncType{ Type: &ast.FuncType{
@ -124,119 +126,150 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
}, },
}, },
Body: &ast.BlockStmt{ Body: &ast.BlockStmt{
List: []ast.Stmt{ List: func() []ast.Stmt {
// item := Item{Description: "an item"} assertNotZero := func(obj *ast.Ident, field string) *ast.ExprStmt {
&ast.AssignStmt{ return &ast.ExprStmt{X: &ast.CallExpr{
Lhs: []ast.Expr{testObj}, Fun: ast.NewIdent("assert.NotZero"),
Tok: token.DEFINE, Args: []ast.Expr{ast.NewIdent("t"), &ast.SelectorExpr{X: obj, Sel: ast.NewIdent(field)}},
Rhs: []ast.Expr{&ast.CompositeLit{ }}
Type: ast.NewIdent(tbl.GoTypeName), }
Elts: []ast.Expr{
&ast.KeyValueExpr{ stmts := []ast.Stmt{
Key: fieldName, // item := Item{Description: "an item"}
Value: &ast.BasicLit{Kind: token.STRING, Value: description1}, &ast.AssignStmt{
Lhs: []ast.Expr{testObj},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CompositeLit{
Type: ast.NewIdent(tbl.GoTypeName),
Elts: []ast.Expr{
&ast.KeyValueExpr{
Key: fieldName,
Value: &ast.BasicLit{Kind: token.STRING, Value: description1},
},
}, },
}},
},
// TestDB.SaveItem(&item)
&ast.ExprStmt{X: &ast.CallExpr{
Fun: ast.NewIdent("TestDB.Save" + tbl.GoTypeName),
Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: testObj}},
}},
// require.NotZero(t, item.ID)
&ast.ExprStmt{X: &ast.CallExpr{
Fun: ast.NewIdent("require.NotZero"),
Args: []ast.Expr{ast.NewIdent("t"), &ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
}},
}
// After create: assert timestamps are set
if hasCreatedAt {
stmts = append(stmts, assertNotZero(testObj, "CreatedAt"))
}
if hasUpdatedAt {
stmts = append(stmts, assertNotZero(testObj, "UpdatedAt"))
}
stmts = append(stmts,
// item2 := Must(TestDB.GetItemByID(item.ID))
&ast.AssignStmt{
Lhs: []ast.Expr{testObj2},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent("Must"),
Args: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent("TestDB.Get" + tbl.GoTypeName + "ByID"),
Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
}},
}},
},
// assert.Equal(t, "an item", item2.Description)
&ast.ExprStmt{X: &ast.CallExpr{
Fun: ast.NewIdent("assert.Equal"),
Args: []ast.Expr{
ast.NewIdent("t"),
&ast.BasicLit{Kind: token.STRING, Value: description1},
&ast.SelectorExpr{X: testObj2, Sel: fieldName},
}, },
}}, }},
},
// TestDB.SaveItem(&item) // item.Description = "a big item"
&ast.ExprStmt{X: &ast.CallExpr{ &ast.AssignStmt{
Fun: ast.NewIdent("TestDB.Save" + tbl.GoTypeName), Lhs: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: fieldName}},
Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: testObj}}, Tok: token.ASSIGN,
}}, Rhs: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: description2}},
},
// require.NotZero(t, item.ID) // TestDB.SaveItem(&item)
&ast.ExprStmt{X: &ast.CallExpr{ &ast.ExprStmt{X: &ast.CallExpr{
Fun: ast.NewIdent("require.NotZero"), Fun: ast.NewIdent("TestDB.Save" + tbl.GoTypeName),
Args: []ast.Expr{ast.NewIdent("t"), &ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}}, Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: testObj}},
}}, }},
)
// item2 := Must(TestDB.GetItemByID(item.ID)) // After update: assert timestamps are still set
&ast.AssignStmt{ if hasCreatedAt {
Lhs: []ast.Expr{testObj2}, stmts = append(stmts, assertNotZero(testObj, "CreatedAt"))
Tok: token.DEFINE, }
Rhs: []ast.Expr{&ast.CallExpr{ if hasUpdatedAt {
Fun: ast.NewIdent("Must"), stmts = append(stmts, assertNotZero(testObj, "UpdatedAt"))
Args: []ast.Expr{&ast.CallExpr{ }
stmts = append(stmts,
// item2 = Must(TestDB.GetItemByID(item.ID))
&ast.AssignStmt{
Lhs: []ast.Expr{testObj2},
Tok: token.ASSIGN,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent("Must"),
Args: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent("TestDB.Get" + tbl.GoTypeName + "ByID"),
Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
}},
}},
},
// assert.Equal(t, item.Description, item2.Description)
&ast.ExprStmt{X: &ast.CallExpr{
Fun: ast.NewIdent("assert.Equal"),
Args: []ast.Expr{
ast.NewIdent("t"),
&ast.SelectorExpr{X: testObj, Sel: fieldName},
&ast.SelectorExpr{X: testObj2, Sel: fieldName},
},
}},
// TestDB.DeleteItem(item)
&ast.ExprStmt{X: &ast.CallExpr{
Fun: ast.NewIdent("TestDB.Delete" + tbl.GoTypeName),
Args: []ast.Expr{testObj},
}},
// _, err := TestDB.GetItemByID(item.ID)
&ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent("_"), ast.NewIdent("err")},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent("TestDB.Get" + tbl.GoTypeName + "ByID"), Fun: ast.NewIdent("TestDB.Get" + tbl.GoTypeName + "ByID"),
Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}}, Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
}}, }},
}},
},
// assert.Equal(t, "an item", item2.Description)
&ast.ExprStmt{X: &ast.CallExpr{
Fun: ast.NewIdent("assert.Equal"),
Args: []ast.Expr{
ast.NewIdent("t"),
&ast.BasicLit{Kind: token.STRING, Value: description1},
&ast.SelectorExpr{X: testObj2, Sel: fieldName},
}, },
}},
// item.Description = "a big item" // assert.ErrorIs(t, err, db.ErrNotInDB)
&ast.AssignStmt{ &ast.ExprStmt{X: &ast.CallExpr{
Lhs: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: fieldName}}, Fun: ast.NewIdent("assert.ErrorIs"),
Tok: token.ASSIGN, Args: []ast.Expr{
Rhs: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: description2}}, ast.NewIdent("t"),
}, ast.NewIdent("err"),
ast.NewIdent("ErrNotInDB"),
// TestDB.SaveItem(&item) },
&ast.ExprStmt{X: &ast.CallExpr{
Fun: ast.NewIdent("TestDB.Save" + tbl.GoTypeName),
Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: testObj}},
}},
// item2 = Must(TestDB.GetItemByID(item.ID))
&ast.AssignStmt{
Lhs: []ast.Expr{testObj2},
Tok: token.ASSIGN,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent("Must"),
Args: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent("TestDB.Get" + tbl.GoTypeName + "ByID"),
Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
}},
}}, }},
}, )
// assert.Equal(t, item.Description, item2.Description) return stmts
&ast.ExprStmt{X: &ast.CallExpr{ }(),
Fun: ast.NewIdent("assert.Equal"),
Args: []ast.Expr{
ast.NewIdent("t"),
&ast.SelectorExpr{X: testObj, Sel: fieldName},
&ast.SelectorExpr{X: testObj2, Sel: fieldName},
},
}},
// TestDB.DeleteItem(item)
&ast.ExprStmt{X: &ast.CallExpr{
Fun: ast.NewIdent("TestDB.Delete" + tbl.GoTypeName),
Args: []ast.Expr{testObj},
}},
// _, err := TestDB.GetItemByID(item.ID)
&ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent("_"), ast.NewIdent("err")},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent("TestDB.Get" + tbl.GoTypeName + "ByID"),
Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
}},
},
// assert.ErrorIs(t, err, db.ErrNotInDB)
&ast.ExprStmt{X: &ast.CallExpr{
Fun: ast.NewIdent("assert.ErrorIs"),
Args: []ast.Expr{
ast.NewIdent("t"),
ast.NewIdent("err"),
ast.NewIdent("ErrNotInDB"),
},
}},
},
}, },
} }