codegen: add foreign key error check test
This commit is contained in:
parent
3d357abb93
commit
1c56661560
@ -9,7 +9,8 @@ set -e
|
|||||||
set -x
|
set -x
|
||||||
|
|
||||||
PS4='+(${BASH_SOURCE}:${LINENO}): '
|
PS4='+(${BASH_SOURCE}:${LINENO}): '
|
||||||
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
proj_root=$(readlink -f "$(dirname "${BASH_SOURCE[0]}")/..")
|
||||||
|
cd "$proj_root"
|
||||||
|
|
||||||
# Compile `gas`
|
# Compile `gas`
|
||||||
gas="/tmp/gas"
|
gas="/tmp/gas"
|
||||||
@ -29,6 +30,9 @@ EOF
|
|||||||
|
|
||||||
cd $test_project
|
cd $test_project
|
||||||
|
|
||||||
|
# Add "replace" directive"
|
||||||
|
echo "replace git.offline-twitter.com/offline-labs/gas-stack => $proj_root" >> go.mod
|
||||||
|
|
||||||
# Create a new table in the schema
|
# Create a new table in the schema
|
||||||
cat >> pkg/db/schema.sql <<EOF
|
cat >> pkg/db/schema.sql <<EOF
|
||||||
create table item_flavor (rowid integer primary key, name text not null) strict;
|
create table item_flavor (rowid integer primary key, name text not null) strict;
|
||||||
|
|||||||
@ -214,7 +214,7 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
&ast.CallExpr{
|
&ast.CallExpr{
|
||||||
Fun: ast.NewIdent("NewForeignKeyError"),
|
Fun: ast.NewIdent("NewForeignKeyError"),
|
||||||
Args: []ast.Expr{
|
Args: []ast.Expr{
|
||||||
ast.NewIdent(`"Type"`),
|
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", structFieldName)},
|
||||||
ast.NewIdent(fmt.Sprintf("%q", col.ForeignKeyTargetTable)),
|
ast.NewIdent(fmt.Sprintf("%q", col.ForeignKeyTargetTable)),
|
||||||
structField,
|
structField,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -83,6 +83,30 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func MakeItem() Item { return Item{} }
|
||||||
|
makeItemFunc := &ast.FuncDecl{
|
||||||
|
Name: ast.NewIdent("Make" + tbl.GoTypeName),
|
||||||
|
Type: &ast.FuncType{
|
||||||
|
Params: &ast.FieldList{},
|
||||||
|
Results: &ast.FieldList{
|
||||||
|
List: []*ast.Field{
|
||||||
|
{Type: ast.NewIdent(tbl.GoTypeName)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Body: &ast.BlockStmt{
|
||||||
|
List: []ast.Stmt{
|
||||||
|
&ast.ReturnStmt{
|
||||||
|
Results: []ast.Expr{
|
||||||
|
&ast.CompositeLit{
|
||||||
|
Type: ast.NewIdent(tbl.GoTypeName),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
testObj := ast.NewIdent("item")
|
testObj := ast.NewIdent("item")
|
||||||
testObj2 := ast.NewIdent("item2")
|
testObj2 := ast.NewIdent("item2")
|
||||||
fieldName := ast.NewIdent("Description")
|
fieldName := ast.NewIdent("Description")
|
||||||
@ -209,7 +233,7 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
|
|||||||
Args: []ast.Expr{
|
Args: []ast.Expr{
|
||||||
ast.NewIdent("t"),
|
ast.NewIdent("t"),
|
||||||
ast.NewIdent("err"),
|
ast.NewIdent("err"),
|
||||||
&ast.SelectorExpr{X: ast.NewIdent("db"), Sel: ast.NewIdent("ErrNotInDB")},
|
ast.NewIdent("ErrNotInDB"),
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
@ -237,9 +261,126 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shouldIncludeTestFkCheck := false
|
||||||
|
testFkChecking := &ast.FuncDecl{
|
||||||
|
Name: ast.NewIdent("Test" + tbl.GoTypeName + "FkChecking"),
|
||||||
|
Type: &ast.FuncType{
|
||||||
|
Params: &ast.FieldList{
|
||||||
|
List: []*ast.Field{
|
||||||
|
{
|
||||||
|
Names: []*ast.Ident{ast.NewIdent("t")},
|
||||||
|
Type: &ast.StarExpr{
|
||||||
|
X: &ast.SelectorExpr{
|
||||||
|
X: ast.NewIdent("testing"),
|
||||||
|
Sel: ast.NewIdent("T"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Body: &ast.BlockStmt{
|
||||||
|
List: func() []ast.Stmt {
|
||||||
|
// post := MakePost()
|
||||||
|
stmts := []ast.Stmt{
|
||||||
|
&ast.AssignStmt{
|
||||||
|
Lhs: []ast.Expr{ast.NewIdent(tbl.VarName)},
|
||||||
|
Tok: token.DEFINE,
|
||||||
|
Rhs: []ast.Expr{
|
||||||
|
&ast.CallExpr{
|
||||||
|
Fun: ast.NewIdent("Make" + tbl.GoTypeName),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, col := range tbl.Columns {
|
||||||
|
if col.IsForeignKey {
|
||||||
|
shouldIncludeTestFkCheck = true
|
||||||
|
stmts = append(stmts, []ast.Stmt{
|
||||||
|
|
||||||
|
// post.QuotedPostID = 94354538969386985
|
||||||
|
&ast.AssignStmt{
|
||||||
|
Lhs: []ast.Expr{
|
||||||
|
&ast.SelectorExpr{
|
||||||
|
X: ast.NewIdent(tbl.VarName),
|
||||||
|
Sel: ast.NewIdent(fkFieldName(col)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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: token.DEFINE,
|
||||||
|
Rhs: []ast.Expr{
|
||||||
|
&ast.CallExpr{
|
||||||
|
Fun: &ast.SelectorExpr{
|
||||||
|
X: ast.NewIdent("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", fkFieldName(col)),
|
||||||
|
},
|
||||||
|
&ast.SelectorExpr{
|
||||||
|
X: ast.NewIdent(tbl.VarName),
|
||||||
|
Sel: ast.NewIdent(fkFieldName(col)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stmts
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testList := []ast.Decl{
|
||||||
|
// var TestDB *DB
|
||||||
|
testDBDecl,
|
||||||
|
makeItemFunc,
|
||||||
|
|
||||||
|
// func init() { TestDB = MakeDB("tmp") }
|
||||||
|
initFuncDecl,
|
||||||
|
|
||||||
|
// func MakeDB(dbName string) *DB { db := Must(Create(fmt.Sprintf("file:%s?mode=memory&cache=shared", dbName))); return db }
|
||||||
|
makeDBHelperDecl,
|
||||||
|
|
||||||
|
testCreateUpdateDelete,
|
||||||
|
testGetAll,
|
||||||
|
}
|
||||||
|
if shouldIncludeTestFkCheck {
|
||||||
|
testList = append(testList, testFkChecking)
|
||||||
|
}
|
||||||
return &ast.File{
|
return &ast.File{
|
||||||
Name: ast.NewIdent(testpackageName),
|
Name: ast.NewIdent(testpackageName),
|
||||||
Decls: []ast.Decl{
|
Decls: append([]ast.Decl{
|
||||||
&ast.GenDecl{
|
&ast.GenDecl{
|
||||||
Tok: token.IMPORT,
|
Tok: token.IMPORT,
|
||||||
Specs: []ast.Spec{
|
Specs: []ast.Spec{
|
||||||
@ -247,7 +388,7 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
|
|||||||
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"testing"`}},
|
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"testing"`}},
|
||||||
&ast.ImportSpec{
|
&ast.ImportSpec{
|
||||||
Path: &ast.BasicLit{Kind: token.STRING, Value: `"git.offline-twitter.com/offline-labs/gas-stack/pkg/db"`},
|
Path: &ast.BasicLit{Kind: token.STRING, Value: `"git.offline-twitter.com/offline-labs/gas-stack/pkg/db"`},
|
||||||
Name: ast.NewIdent("db"),
|
Name: ast.NewIdent("."),
|
||||||
},
|
},
|
||||||
&ast.ImportSpec{
|
&ast.ImportSpec{
|
||||||
Path: &ast.BasicLit{Kind: token.STRING, Value: `"git.offline-twitter.com/offline-labs/gas-stack/pkg/flowutils"`},
|
Path: &ast.BasicLit{Kind: token.STRING, Value: `"git.offline-twitter.com/offline-labs/gas-stack/pkg/flowutils"`},
|
||||||
@ -261,17 +402,6 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
|
|||||||
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"github.com/stretchr/testify/require"`}},
|
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"github.com/stretchr/testify/require"`}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// var TestDB *DB
|
}, testList...),
|
||||||
testDBDecl,
|
|
||||||
|
|
||||||
// func init() { TestDB = MakeDB("tmp") }
|
|
||||||
initFuncDecl,
|
|
||||||
|
|
||||||
// func MakeDB(dbName string) *DB { db := Must(Create(fmt.Sprintf("file:%s?mode=memory&cache=shared", dbName))); return db }
|
|
||||||
makeDBHelperDecl,
|
|
||||||
|
|
||||||
testCreateUpdateDelete,
|
|
||||||
testGetAll,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
pkg/db/errors_test_helper.go
Normal file
21
pkg/db/errors_test_helper.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AssertForeignKeyError[T ForeignKey](t *testing.T, err error, field string, val T) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
var fkErr ForeignKeyError[T]
|
||||||
|
require.Error(t, err)
|
||||||
|
require.ErrorIs(t, err, ErrForeignKeyViolation)
|
||||||
|
// ErrorAs produces terrible error messages if it's a ForeignKeyError with a different type
|
||||||
|
// parameter (i.e., if it was a different field that failed).
|
||||||
|
require.ErrorAs(t, err, &fkErr, "expected error field: %q", field)
|
||||||
|
assert.Equal(t, field, fkErr.Field)
|
||||||
|
assert.Equal(t, val, fkErr.FkValue)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user