10 Commits

Author SHA1 Message Date
616304c7dd ci: add support for manually triggering the build
All checks were successful
CI / build-docker (push) Successful in 5s
CI / build-docker-bootstrap (push) Has been skipped
CI / release-test (push) Successful in 4m14s
2026-07-12 14:05:05 -07:00
17fc8a68f6 codegen: wrap foreign key checks for nullable FKs in "if a.Val != 0 { ... }" 2026-07-12 13:34:21 -07:00
d572745613 codegen: don't skip created_at auto-timestamp for 'without rowid' tables 2026-07-12 13:20:35 -07:00
9bfb31798c codegen: don't auto-timestamp overwrite provided timestamps if there are ones 2026-07-07 12:15:51 -07:00
eafeb658bd codegen: fix invalid SQL query being generated for GetItemBy with multiple params 2026-06-24 13:59:13 -07:00
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
9a11f3986c codegen: improve the "TestFkChecking" generated function
All checks were successful
CI / build-docker (push) Successful in 6s
CI / build-docker-bootstrap (push) Has been skipped
CI / release-test (push) Successful in 3m22s
2026-04-24 14:05:48 -07:00
3 changed files with 269 additions and 179 deletions

View File

@@ -1,6 +1,6 @@
name: CI name: CI
on: [push] on: [push, workflow_dispatch]
jobs: jobs:
# These steps build the `gas` docker image. # These steps build the `gas` docker image.

View File

@@ -161,79 +161,92 @@ func buildFKCheckLambda(tbl schema.Table) (*ast.AssignStmt, bool) {
structFieldName := col.GoFieldName() structFieldName := col.GoFieldName()
structField := &ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent(structFieldName)} structField := &ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent(structFieldName)}
if col.IsNonCodeTableForeignKey() { ret = append(ret, func() ast.Stmt {
// Real foreign key; look up referent by ID to see if it exists // Wrap nullable FKs in "if a.val != 0 { ... }"
ret = append(ret, &ast.IfStmt{ wrap := func(input ast.Stmt) ast.Stmt {
Init: &ast.AssignStmt{ if col.IsNullableForeignKey() {
Lhs: []ast.Expr{ast.NewIdent("_"), ast.NewIdent("err")}, return &ast.IfStmt{
Tok: token.DEFINE, Cond: &ast.BinaryExpr{X: structField, Op: token.NEQ, Y: &ast.BasicLit{Kind: token.INT, Value: "0"}},
Rhs: []ast.Expr{ Body: &ast.BlockStmt{List: []ast.Stmt{input}},
&ast.CallExpr{ }
Fun: &ast.SelectorExpr{X: ast.NewIdent("db"), Sel: ast.NewIdent(getByIDFuncName(col.ForeignKeyTargetTable))}, } else {
Args: []ast.Expr{structField}, return input
}
}
if col.IsNonCodeTableForeignKey() {
// Real foreign key; look up referent by ID to see if it exists
return wrap(&ast.IfStmt{
Init: &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent("_"), ast.NewIdent("err")},
Tok: token.DEFINE,
Rhs: []ast.Expr{
&ast.CallExpr{
Fun: &ast.SelectorExpr{X: ast.NewIdent("db"), Sel: ast.NewIdent(getByIDFuncName(col.ForeignKeyTargetTable))},
Args: []ast.Expr{structField},
},
}, },
}, },
}, Cond: &ast.CallExpr{
Cond: &ast.CallExpr{ Fun: &ast.SelectorExpr{X: ast.NewIdent("errors"), Sel: ast.NewIdent("Is")},
Fun: &ast.SelectorExpr{X: ast.NewIdent("errors"), Sel: ast.NewIdent("Is")}, Args: []ast.Expr{ast.NewIdent("err"), ast.NewIdent("ErrNotInDB")},
Args: []ast.Expr{ast.NewIdent("err"), ast.NewIdent("ErrNotInDB")}, },
}, Body: &ast.BlockStmt{
Body: &ast.BlockStmt{ List: []ast.Stmt{
List: []ast.Stmt{ &ast.ReturnStmt{
&ast.ReturnStmt{ Results: []ast.Expr{
Results: []ast.Expr{ &ast.CallExpr{
&ast.CallExpr{ Fun: ast.NewIdent("NewForeignKeyError"),
Fun: ast.NewIdent("NewForeignKeyError"), Args: []ast.Expr{
Args: []ast.Expr{ &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", structFieldName)},
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", structFieldName)}, &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", col.ForeignKeyTargetTable)},
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", col.ForeignKeyTargetTable)}, structField,
structField, },
}, },
}, },
}, },
}, },
}, },
}, })
}) } else {
} else { // Code table value. Query the table to see if it exists
// Code table value. Query the table to see if it exists return wrap(&ast.IfStmt{
ret = append(ret, &ast.IfStmt{ Init: &ast.AssignStmt{
Init: &ast.AssignStmt{ Lhs: []ast.Expr{ast.NewIdent("err")},
Lhs: []ast.Expr{ast.NewIdent("err")}, Tok: token.DEFINE,
Tok: token.ASSIGN, Rhs: []ast.Expr{
Rhs: []ast.Expr{ &ast.CallExpr{
&ast.CallExpr{ Fun: &ast.SelectorExpr{X: dbDB, Sel: ast.NewIdent("Get")},
Fun: &ast.SelectorExpr{X: dbDB, Sel: ast.NewIdent("Get")}, Args: []ast.Expr{
Args: []ast.Expr{ &ast.CallExpr{Fun: ast.NewIdent("new"), Args: []ast.Expr{ast.NewIdent("int")}},
&ast.CallExpr{Fun: ast.NewIdent("new"), Args: []ast.Expr{ast.NewIdent("int")}}, &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("`select 1 from %s where rowid = ?`", col.ForeignKeyTargetTable)},
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("`select 1 from %s where rowid = ?`", col.ForeignKeyTargetTable)}, structField,
structField, },
}, },
}, },
}, },
}, Cond: &ast.CallExpr{
Cond: &ast.CallExpr{ Fun: &ast.SelectorExpr{X: ast.NewIdent("errors"), Sel: ast.NewIdent("Is")},
Fun: &ast.SelectorExpr{X: ast.NewIdent("errors"), Sel: ast.NewIdent("Is")}, Args: []ast.Expr{ast.NewIdent("err"), &ast.SelectorExpr{X: ast.NewIdent("sql"), Sel: ast.NewIdent("ErrNoRows")}},
Args: []ast.Expr{ast.NewIdent("err"), &ast.SelectorExpr{X: ast.NewIdent("sql"), Sel: ast.NewIdent("ErrNoRows")}}, },
}, Body: &ast.BlockStmt{
Body: &ast.BlockStmt{ List: []ast.Stmt{
List: []ast.Stmt{ &ast.ReturnStmt{
&ast.ReturnStmt{ Results: []ast.Expr{
Results: []ast.Expr{ &ast.CallExpr{
&ast.CallExpr{ Fun: ast.NewIdent("NewForeignKeyError"),
Fun: ast.NewIdent("NewForeignKeyError"), Args: []ast.Expr{
Args: []ast.Expr{ &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", structFieldName)},
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", structFieldName)}, &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", col.ForeignKeyTargetTable)},
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("%q", col.ForeignKeyTargetTable)}, structField,
structField, },
}, },
}, },
}, },
}, },
}, },
}, })
}) }
} }())
} }
// final return nil // final return nil
ret = append(ret, &ast.ReturnStmt{Results: []ast.Expr{ast.NewIdent("nil")}}) ret = append(ret, &ast.ReturnStmt{Results: []ast.Expr{ast.NewIdent("nil")}})
@@ -386,6 +399,24 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
} }
if tbl.IsWithoutRowid { if tbl.IsWithoutRowid {
if hasCreatedAt {
// Auto-timestamps: created_at. Don't overwrite existing timestamps (e.g., data import / migrations)
ret = append(ret, &ast.IfStmt{
Cond: &ast.CallExpr{Fun: &ast.SelectorExpr{
X: &ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("CreatedAt")},
Sel: ast.NewIdent("IsZero"),
}},
Body: &ast.BlockStmt{
List: []ast.Stmt{
&ast.AssignStmt{
Lhs: []ast.Expr{&ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("CreatedAt")}},
Tok: token.ASSIGN,
Rhs: []ast.Expr{&ast.CallExpr{Fun: ast.NewIdent("TimestampNow"), Args: []ast.Expr{}}},
},
},
},
})
}
ret = append(ret, namedExecStmt(upsertStmt)...) ret = append(ret, namedExecStmt(upsertStmt)...)
ret = append(ret, PanicIfRowsAffected(tbl)) ret = append(ret, PanicIfRowsAffected(tbl))
} else { } else {
@@ -403,10 +434,21 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
ret1 := []ast.Stmt{Comment("Do create")} ret1 := []ast.Stmt{Comment("Do create")}
if hasCreatedAt { if hasCreatedAt {
// Auto-timestamps: created_at // Auto-timestamps: created_at
ret1 = append(ret1, &ast.AssignStmt{ ret1 = append(ret1, &ast.IfStmt{
Lhs: []ast.Expr{&ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("CreatedAt")}}, // Don't overwrite existing timestamps. This is useful for various reasons, e.g., data import / migrations
Tok: token.ASSIGN, Cond: &ast.CallExpr{Fun: &ast.SelectorExpr{
Rhs: []ast.Expr{&ast.CallExpr{Fun: ast.NewIdent("TimestampNow"), Args: []ast.Expr{}}}, X: &ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("CreatedAt")},
Sel: ast.NewIdent("IsZero"),
}},
Body: &ast.BlockStmt{
List: []ast.Stmt{
&ast.AssignStmt{
Lhs: []ast.Expr{&ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("CreatedAt")}},
Tok: token.ASSIGN,
Rhs: []ast.Expr{&ast.CallExpr{Fun: ast.NewIdent("TimestampNow"), Args: []ast.Expr{}}},
},
},
},
}) })
} }
return append(ret1, namedExecStmt(insertStmt)...) return append(ret1, namedExecStmt(insertStmt)...)
@@ -477,7 +519,7 @@ func GenerateGetItemBy(tbl schema.Table, cols []schema.Column) *ast.FuncDecl {
for _, col := range cols { for _, col := range cols {
funcParam := ast.NewIdent(col.LongGoVarName()) funcParam := ast.NewIdent(col.LongGoVarName())
funcParams.List = append(funcParams.List, &ast.Field{Names: []*ast.Ident{funcParam}, Type: GoTypeForColumn(col)}) funcParams.List = append(funcParams.List, &ast.Field{Names: []*ast.Ident{funcParam}, Type: GoTypeForColumn(col)})
colNames = append(colNames, fmt.Sprintf("%s = :%s", col.Name, col.Name)) colNames = append(colNames, fmt.Sprintf("%s = ?", col.Name))
funcNameSuffix = append(funcNameSuffix, col.GoFieldName()) funcNameSuffix = append(funcNameSuffix, col.GoFieldName())
sqlParams = append(sqlParams, funcParam) sqlParams = append(sqlParams, funcParam)
} }
@@ -489,7 +531,7 @@ func GenerateGetItemBy(tbl schema.Table, cols []schema.Column) *ast.FuncDecl {
Y: SQLFieldsConstIdent(tbl), Y: SQLFieldsConstIdent(tbl),
}, },
Op: token.ADD, Op: token.ADD,
Y: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("`\n\t from %s\n\t where %s = ?\n\t`", tbl.TableName, strings.Join(colNames, " and "))}, Y: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("`\n\t from %s\n\t where %s\n\t`", tbl.TableName, strings.Join(colNames, " and "))},
} }
return &ast.FuncDecl{ return &ast.FuncDecl{
@@ -515,7 +557,8 @@ func GenerateGetItemBy(tbl schema.Table, cols []schema.Column) *ast.FuncDecl {
&ast.IfStmt{ &ast.IfStmt{
Cond: &ast.CallExpr{ Cond: &ast.CallExpr{
Fun: &ast.SelectorExpr{X: ast.NewIdent("errors"), Sel: ast.NewIdent("Is")}, Fun: &ast.SelectorExpr{X: ast.NewIdent("errors"), Sel: ast.NewIdent("Is")},
Args: []ast.Expr{ast.NewIdent("err"), &ast.SelectorExpr{X: ast.NewIdent("sql"), Sel: ast.NewIdent("ErrNoRows")}}}, Args: []ast.Expr{ast.NewIdent("err"), &ast.SelectorExpr{X: ast.NewIdent("sql"), Sel: ast.NewIdent("ErrNoRows")}},
},
Body: &ast.BlockStmt{List: []ast.Stmt{&ast.ReturnStmt{Results: []ast.Expr{&ast.CompositeLit{Type: ast.NewIdent(tbl.GoTypeName)}, ast.NewIdent("ErrNotInDB")}}}}, Body: &ast.BlockStmt{List: []ast.Stmt{&ast.ReturnStmt{Results: []ast.Expr{&ast.CompositeLit{Type: ast.NewIdent(tbl.GoTypeName)}, ast.NewIdent("ErrNotInDB")}}}},
}, },
&ast.ReturnStmt{}, &ast.ReturnStmt{},

View File

@@ -63,12 +63,52 @@ 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 fieldName := ast.NewIdent("Description") // TODO
description1 := `"an item"` description1 := "an item"
description2 := `"a big item"` description2 := "a big item"
testDB := ast.NewIdent("TestDB") testDB := ast.NewIdent("TestDB")
hasCreatedAt, hasUpdatedAt := tbl.HasAutoTimestamps() 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{ testFuncType := &ast.FuncType{
Params: &ast.FieldList{ Params: &ast.FieldList{
List: []*ast.Field{{ 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{ testCreateUpdateDelete := &ast.FuncDecl{
Name: ast.NewIdent("TestCreateUpdateDelete" + tbl.GoTypeName), Name: ast.NewIdent("TestCreateUpdateDelete" + tbl.GoTypeName),
Type: testFuncType, Type: testFuncType,
@@ -116,11 +253,21 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
}, },
}, },
// TestDB.SaveItem(&item) // TestDB.SaveItem(&item), possibly with error check
&ast.ExprStmt{X: &ast.CallExpr{ &ast.ExprStmt{X: func() *ast.CallExpr {
Fun: &ast.SelectorExpr{X: testDB, Sel: ast.NewIdent("Save" + tbl.GoTypeName)}, mainExpr := &ast.CallExpr{
Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: testObj}}, 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) // require.NotZero(t, item.ID)
&ast.ExprStmt{X: &ast.CallExpr{ &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) // if deep.Equal(...) {...}
&ast.ExprStmt{X: &ast.CallExpr{ makeDeepEqual(testObj, testObj2),
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},
},
}},
) )
stmts = append(stmts, stmts = append(stmts,
@@ -170,7 +310,7 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
&ast.AssignStmt{ &ast.AssignStmt{
Lhs: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: fieldName}}, Lhs: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: fieldName}},
Tok: token.ASSIGN, 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) // TestDB.SaveItem(&item)
@@ -189,15 +329,8 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
})}, })},
}, },
// assert.Equal(t, item.Description, item2.Description) // if deep.Equal(...) {...}
&ast.ExprStmt{X: &ast.CallExpr{ makeDeepEqual(testObj, testObj2),
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},
},
}},
) )
indexGets, hasIndexedGets := []ast.Stmt{ indexGets, hasIndexedGets := []ast.Stmt{
@@ -292,93 +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() []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),
},
},
},
}
shouldDefineErr := true
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(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}[shouldDefineErr],
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()),
},
},
},
},
}...)
shouldDefineErr = false
}
}
return stmts
}(),
},
}
testList := []ast.Decl{ testList := []ast.Decl{
makeItemFunc, makeItemFunc,
testCreateUpdateDelete, testCreateUpdateDelete,
@@ -406,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)}, Path: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf(`"%s/pkg/%s"`, gomodName, packageName)},
Name: ast.NewIdent("."), 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/assert"`}},
&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"`}},
}, },