Compare commits
4 Commits
v0.0.5
...
99b57c88b0
| Author | SHA1 | Date | |
|---|---|---|---|
| 99b57c88b0 | |||
| 6cf0b66dd5 | |||
| f98f8416e8 | |||
| 1592c7d1fc |
@@ -1,6 +1,6 @@
|
|||||||
# A dev container.
|
# A dev container.
|
||||||
|
|
||||||
FROM alpine:3.22
|
FROM alpine:3.24
|
||||||
|
|
||||||
RUN apk add build-base git go sqlite vim shellcheck curl jq sudo bash docker
|
RUN apk add build-base git go sqlite vim shellcheck curl jq sudo bash docker
|
||||||
|
|
||||||
|
|||||||
@@ -296,9 +296,8 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
updatePairs := make([]string, 0, len(tbl.Columns))
|
updatePairs := make([]string, 0, len(tbl.Columns))
|
||||||
|
|
||||||
hasCreatedAt, hasUpdatedAt := tbl.HasAutoTimestamps()
|
hasCreatedAt, hasUpdatedAt := tbl.HasAutoTimestamps()
|
||||||
hasSoftDelete := tbl.HasSoftDelete()
|
|
||||||
|
|
||||||
// Assemble data for building SQL "insert" and "update" strings
|
// Assemble SQL "insert" string
|
||||||
for _, col := range tbl.Columns {
|
for _, col := range tbl.Columns {
|
||||||
if col.Name == "rowid" {
|
if col.Name == "rowid" {
|
||||||
continue
|
continue
|
||||||
@@ -309,23 +308,20 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
val = fmt.Sprintf("nullif(%s, 0)", val)
|
val = fmt.Sprintf("nullif(%s, 0)", val)
|
||||||
}
|
}
|
||||||
insertVals = append(insertVals, val)
|
insertVals = append(insertVals, val)
|
||||||
// created_at should not be updated after creation
|
|
||||||
if col.Name == "created_at" && hasCreatedAt {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Soft-delete state is managed by SoftDeleteXyz(), not by updates
|
|
||||||
if hasSoftDelete && (col.Name == "is_deleted" || col.Name == "deleted_at") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !col.IsPrimaryKey { // Don't try to update primary key columns (mainly for w/o rowid tables)
|
|
||||||
updatePairs = append(updatePairs, col.Name+"="+val)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
insertStmt := fmt.Sprintf("\n\t\t insert into %s (%s)\n\t\t values (%s)\n\t\t",
|
insertStmt := fmt.Sprintf("\n\t\t insert into %s (%s)\n\t\t values (%s)\n\t\t",
|
||||||
tbl.TableName,
|
tbl.TableName,
|
||||||
strings.Join(insertCols, ", "),
|
strings.Join(insertCols, ", "),
|
||||||
strings.Join(insertVals, ", "),
|
strings.Join(insertVals, ", "),
|
||||||
)
|
)
|
||||||
|
// ...and "update" string
|
||||||
|
for _, col := range tbl.GetUpdateableColumns() {
|
||||||
|
val := ":" + col.Name
|
||||||
|
if col.IsNullableForeignKey() {
|
||||||
|
val = fmt.Sprintf("nullif(%s, 0)", val)
|
||||||
|
}
|
||||||
|
updatePairs = append(updatePairs, col.Name+"="+val)
|
||||||
|
}
|
||||||
updateStmt := fmt.Sprintf("\n\t\t update %s\n\t\t set %s\n\t\t where rowid = :rowid\n\t\t",
|
updateStmt := fmt.Sprintf("\n\t\t update %s\n\t\t set %s\n\t\t where rowid = :rowid\n\t\t",
|
||||||
tbl.TableName,
|
tbl.TableName,
|
||||||
strings.Join(updatePairs, ",\n\t\t "),
|
strings.Join(updatePairs, ",\n\t\t "),
|
||||||
|
|||||||
@@ -67,26 +67,18 @@ func SampleValue(c pkgschema.Column, offset int) ast.Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateTestFields returns the columns for which the test generator should assign its own
|
// 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
|
// sample values in MakeXyz() and the create/update test. It starts with Table.GetUpdateableColumns()
|
||||||
// that GenerateSaveItemFunc's "update" branch writes to, minus foreign keys (which can't be
|
// and removes fields the generated test should not assign directly: foreign keys and auto-managed
|
||||||
// given plausible values here), the auto-managed "created_at"/"updated_at" columns, and the
|
// updated_at.
|
||||||
// soft-delete columns (which SoftDeleteXyz() manages).
|
|
||||||
func UpdateTestFields(tbl pkgschema.Table) (ret []pkgschema.Column) {
|
func UpdateTestFields(tbl pkgschema.Table) (ret []pkgschema.Column) {
|
||||||
hasCreatedAt, hasUpdatedAt := tbl.HasAutoTimestamps()
|
_, hasUpdatedAt := tbl.HasAutoTimestamps()
|
||||||
hasSoftDelete := tbl.HasSoftDelete()
|
for _, c := range tbl.GetUpdateableColumns() {
|
||||||
for _, c := range tbl.Columns {
|
if c.IsForeignKey {
|
||||||
if c.Name == "rowid" || c.IsPrimaryKey || c.IsForeignKey {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if c.Name == "created_at" && hasCreatedAt {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if c.Name == "updated_at" && hasUpdatedAt {
|
if c.Name == "updated_at" && hasUpdatedAt {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if hasSoftDelete && (c.Name == "is_deleted" || c.Name == "deleted_at") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ret = append(ret, c)
|
ret = append(ret, c)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -101,12 +93,55 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
|||||||
|
|
||||||
hasCreatedAt, hasUpdatedAt := tbl.HasAutoTimestamps()
|
hasCreatedAt, hasUpdatedAt := tbl.HasAutoTimestamps()
|
||||||
updateTestFields := UpdateTestFields(tbl)
|
updateTestFields := UpdateTestFields(tbl)
|
||||||
|
uniqueColumns := map[string]bool{}
|
||||||
|
for _, index := range schema.Indexes {
|
||||||
|
if index.TableName != tbl.TableName || !index.IsUnique {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, columnName := range index.Columns {
|
||||||
|
uniqueColumns[columnName] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hasRandomUniqueField := false
|
||||||
|
for _, c := range updateTestFields {
|
||||||
|
if uniqueColumns[c.Name] {
|
||||||
|
hasRandomUniqueField = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requiredForeignKeys := []pkgschema.Column{}
|
||||||
|
for _, c := range tbl.Columns {
|
||||||
|
// Only ordinary object foreign keys use a distinct TargetObjectID Go type;
|
||||||
|
// code-table foreign keys retain their underlying SQLite type in the generated model.
|
||||||
|
if c.IsNotNull && c.IsNonCodeTableForeignKey() {
|
||||||
|
requiredForeignKeys = append(requiredForeignKeys, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requiredForeignKeyParamNames := paramNamesFor(requiredForeignKeys)
|
||||||
|
|
||||||
|
// Calls made by this generated test do not create related objects themselves. Pass a
|
||||||
|
// zero ID so the generated test remains syntactically valid; callers of MakeXyz provide
|
||||||
|
// IDs of the related objects they create.
|
||||||
|
makeItemArgs := func() (ret []ast.Expr) {
|
||||||
|
for range requiredForeignKeys {
|
||||||
|
ret = append(ret, &ast.BasicLit{Kind: token.INT, Value: "0"})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}()
|
||||||
|
|
||||||
// func MakeItem() Item { return Item{} }
|
// func MakeItem() Item { return Item{} }
|
||||||
makeItemFunc := &ast.FuncDecl{
|
makeItemFunc := &ast.FuncDecl{
|
||||||
Name: makeHelperName,
|
Name: makeHelperName,
|
||||||
Type: &ast.FuncType{
|
Type: &ast.FuncType{
|
||||||
Params: &ast.FieldList{},
|
Params: &ast.FieldList{List: func() (ret []*ast.Field) {
|
||||||
|
for i, c := range requiredForeignKeys {
|
||||||
|
ret = append(ret, &ast.Field{
|
||||||
|
Names: []*ast.Ident{ast.NewIdent(requiredForeignKeyParamNames[i])},
|
||||||
|
Type: GoTypeForColumn(c),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}()},
|
||||||
Results: &ast.FieldList{
|
Results: &ast.FieldList{
|
||||||
List: []*ast.Field{
|
List: []*ast.Field{
|
||||||
{Type: ast.NewIdent(tbl.GoTypeName)},
|
{Type: ast.NewIdent(tbl.GoTypeName)},
|
||||||
@@ -114,24 +149,71 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Body: &ast.BlockStmt{
|
Body: &ast.BlockStmt{
|
||||||
List: []ast.Stmt{
|
List: append(
|
||||||
&ast.ReturnStmt{
|
func() []ast.Stmt {
|
||||||
Results: []ast.Expr{
|
if !hasRandomUniqueField {
|
||||||
&ast.CompositeLit{
|
return nil
|
||||||
Type: ast.NewIdent(tbl.GoTypeName),
|
}
|
||||||
Elts: func() (ret []ast.Expr) {
|
return []ast.Stmt{&ast.AssignStmt{
|
||||||
for _, c := range updateTestFields {
|
Lhs: []ast.Expr{ast.NewIdent("id")},
|
||||||
ret = append(ret, &ast.KeyValueExpr{
|
Tok: token.DEFINE,
|
||||||
Key: ast.NewIdent(c.GoFieldName()),
|
Rhs: []ast.Expr{&ast.CallExpr{Fun: &ast.SelectorExpr{X: ast.NewIdent("rand"), Sel: ast.NewIdent("Int")}}},
|
||||||
Value: SampleValue(c, 0),
|
}}
|
||||||
})
|
}(),
|
||||||
}
|
[]ast.Stmt{
|
||||||
return
|
&ast.ReturnStmt{
|
||||||
}(),
|
Results: []ast.Expr{
|
||||||
|
&ast.CompositeLit{
|
||||||
|
Type: ast.NewIdent(tbl.GoTypeName),
|
||||||
|
Elts: func() (ret []ast.Expr) {
|
||||||
|
// `UpdateTestFields()` skips foreign keys, so this will not cause duplicates
|
||||||
|
for i, c := range requiredForeignKeys {
|
||||||
|
ret = append(ret, &ast.KeyValueExpr{
|
||||||
|
Key: ast.NewIdent(c.GoFieldName()),
|
||||||
|
Value: ast.NewIdent(requiredForeignKeyParamNames[i]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, c := range updateTestFields {
|
||||||
|
value := SampleValue(c, 0)
|
||||||
|
if uniqueColumns[c.Name] {
|
||||||
|
switch c.Type {
|
||||||
|
case "integer", "int":
|
||||||
|
value = ast.NewIdent("id")
|
||||||
|
case "text":
|
||||||
|
value = &ast.CallExpr{
|
||||||
|
Fun: &ast.SelectorExpr{X: ast.NewIdent("fmt"), Sel: ast.NewIdent("Sprintf")},
|
||||||
|
Args: []ast.Expr{
|
||||||
|
&ast.BasicLit{Kind: token.STRING, Value: `"%s-%d"`},
|
||||||
|
SampleValue(c, 0),
|
||||||
|
ast.NewIdent("id"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case "blob":
|
||||||
|
value = &ast.CallExpr{
|
||||||
|
Fun: &ast.ArrayType{Elt: ast.NewIdent("byte")},
|
||||||
|
Args: []ast.Expr{&ast.CallExpr{
|
||||||
|
Fun: &ast.SelectorExpr{X: ast.NewIdent("fmt"), Sel: ast.NewIdent("Sprintf")},
|
||||||
|
Args: []ast.Expr{
|
||||||
|
&ast.BasicLit{Kind: token.STRING, Value: `"%s-%d"`},
|
||||||
|
&ast.CallExpr{Fun: ast.NewIdent("string"), Args: []ast.Expr{SampleValue(c, 0)}},
|
||||||
|
ast.NewIdent("id"),
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = append(ret, &ast.KeyValueExpr{
|
||||||
|
Key: ast.NewIdent(c.GoFieldName()),
|
||||||
|
Value: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}...,
|
||||||
},
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +324,8 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
|||||||
Tok: map[bool]token.Token{true: token.DEFINE, false: token.ASSIGN}[isFirst],
|
Tok: map[bool]token.Token{true: token.DEFINE, false: token.ASSIGN}[isFirst],
|
||||||
Rhs: []ast.Expr{
|
Rhs: []ast.Expr{
|
||||||
&ast.CallExpr{
|
&ast.CallExpr{
|
||||||
Fun: ast.NewIdent("Make" + tbl.GoTypeName),
|
Fun: ast.NewIdent("Make" + tbl.GoTypeName),
|
||||||
|
Args: makeItemArgs,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -329,7 +412,7 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
|||||||
&ast.AssignStmt{
|
&ast.AssignStmt{
|
||||||
Lhs: []ast.Expr{testObj},
|
Lhs: []ast.Expr{testObj},
|
||||||
Tok: token.DEFINE,
|
Tok: token.DEFINE,
|
||||||
Rhs: []ast.Expr{&ast.CallExpr{Fun: makeHelperName, Args: nil}},
|
Rhs: []ast.Expr{&ast.CallExpr{Fun: makeHelperName, Args: makeItemArgs}},
|
||||||
},
|
},
|
||||||
// item.Description = <sample value, offset 1>
|
// item.Description = <sample value, offset 1>
|
||||||
// ...one assignment per updatable field
|
// ...one assignment per updatable field
|
||||||
@@ -395,39 +478,41 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
|||||||
makeDeepEqual(testObj, testObj2),
|
makeDeepEqual(testObj, testObj2),
|
||||||
)
|
)
|
||||||
|
|
||||||
stmts = append(stmts,
|
if len(tbl.GetUpdateableColumns()) > 0 { // Skip if no mutable fields
|
||||||
BlankLine(),
|
stmts = append(stmts,
|
||||||
Comment("Update"),
|
BlankLine(),
|
||||||
)
|
Comment("Update"),
|
||||||
|
)
|
||||||
|
|
||||||
// item.Description = <sample value, offset 2>
|
// item.Description = <sample value, offset 2>
|
||||||
// ...one assignment per updatable field
|
// ...one assignment per updatable field
|
||||||
for _, c := range updateTestFields {
|
for _, c := range updateTestFields {
|
||||||
stmts = append(stmts, &ast.AssignStmt{
|
stmts = append(stmts, &ast.AssignStmt{
|
||||||
Lhs: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent(c.GoFieldName())}},
|
Lhs: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent(c.GoFieldName())}},
|
||||||
Tok: token.ASSIGN,
|
Tok: token.ASSIGN,
|
||||||
Rhs: []ast.Expr{SampleValue(c, 2)},
|
Rhs: []ast.Expr{SampleValue(c, 2)},
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
stmts = append(stmts,
|
||||||
|
// 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}},
|
||||||
|
}},
|
||||||
|
|
||||||
|
// item2 = must.Get(TestDB.GetItemByID(item.ID))
|
||||||
|
&ast.AssignStmt{
|
||||||
|
Lhs: []ast.Expr{testObj2},
|
||||||
|
Tok: token.ASSIGN,
|
||||||
|
Rhs: []ast.Expr{mustCall(getItemByPKCall(testObj))},
|
||||||
|
},
|
||||||
|
|
||||||
|
// if deep.Equal(...) {...}
|
||||||
|
makeDeepEqual(testObj, testObj2),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
stmts = append(stmts,
|
|
||||||
// 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}},
|
|
||||||
}},
|
|
||||||
|
|
||||||
// item2 = must.Get(TestDB.GetItemByID(item.ID))
|
|
||||||
&ast.AssignStmt{
|
|
||||||
Lhs: []ast.Expr{testObj2},
|
|
||||||
Tok: token.ASSIGN,
|
|
||||||
Rhs: []ast.Expr{mustCall(getItemByPKCall(testObj))},
|
|
||||||
},
|
|
||||||
|
|
||||||
// if deep.Equal(...) {...}
|
|
||||||
makeDeepEqual(testObj, testObj2),
|
|
||||||
)
|
|
||||||
|
|
||||||
indexGets, hasIndexedGets := []ast.Stmt{
|
indexGets, hasIndexedGets := []ast.Stmt{
|
||||||
BlankLine(),
|
BlankLine(),
|
||||||
Comment("Indexed lookups"),
|
Comment("Indexed lookups"),
|
||||||
@@ -560,23 +645,33 @@ func GenerateModelTestAST(tbl pkgschema.Table, schema pkgschema.Schema, gomodNam
|
|||||||
Decls: append([]ast.Decl{
|
Decls: append([]ast.Decl{
|
||||||
&ast.GenDecl{
|
&ast.GenDecl{
|
||||||
Tok: token.IMPORT,
|
Tok: token.IMPORT,
|
||||||
Specs: []ast.Spec{
|
Specs: func() []ast.Spec {
|
||||||
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"testing"`}},
|
specs := []ast.Spec{}
|
||||||
&ast.ImportSpec{
|
if hasRandomUniqueField {
|
||||||
Path: &ast.BasicLit{Kind: token.STRING, Value: `"git.offline-twitter.com/offline-labs/gas-stack/pkg/db"`},
|
specs = append(specs,
|
||||||
Name: ast.NewIdent("."),
|
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"fmt"`}},
|
||||||
},
|
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"math/rand"`}},
|
||||||
&ast.ImportSpec{
|
)
|
||||||
Path: &ast.BasicLit{Kind: token.STRING, Value: `"git.offline-twitter.com/offline-labs/gas-stack/pkg/must"`},
|
}
|
||||||
},
|
specs = append(specs, []ast.Spec{
|
||||||
&ast.ImportSpec{
|
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"testing"`}},
|
||||||
Path: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf(`"%s/pkg/%s"`, gomodName, packageName)},
|
&ast.ImportSpec{
|
||||||
Name: ast.NewIdent("."),
|
Path: &ast.BasicLit{Kind: token.STRING, Value: `"git.offline-twitter.com/offline-labs/gas-stack/pkg/db"`},
|
||||||
},
|
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{
|
||||||
&ast.ImportSpec{Path: &ast.BasicLit{Kind: token.STRING, Value: `"github.com/stretchr/testify/require"`}},
|
Path: &ast.BasicLit{Kind: token.STRING, Value: `"git.offline-twitter.com/offline-labs/gas-stack/pkg/must"`},
|
||||||
},
|
},
|
||||||
|
&ast.ImportSpec{
|
||||||
|
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"`}},
|
||||||
|
}...)
|
||||||
|
return specs
|
||||||
|
}(),
|
||||||
},
|
},
|
||||||
}, testList...),
|
}, testList...),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,3 +76,21 @@ func (t Table) HasSoftDelete() bool {
|
|||||||
}
|
}
|
||||||
return hasIsDeleted && hasDeletedAt
|
return hasIsDeleted && hasDeletedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUpdateableColumns returns the columns that should be written to by an "update" operation.
|
||||||
|
func (t Table) GetUpdateableColumns() (ret []Column) {
|
||||||
|
hasCreatedAt, _ := t.HasAutoTimestamps()
|
||||||
|
for _, col := range t.Columns {
|
||||||
|
if col.Name == "rowid" || col.IsPrimaryKey {
|
||||||
|
continue // Rowid / primary key is never updateable
|
||||||
|
}
|
||||||
|
if col.Name == "created_at" && hasCreatedAt {
|
||||||
|
continue // Update shouldn't write `created_at`
|
||||||
|
}
|
||||||
|
if t.HasSoftDelete() && (col.Name == "is_deleted" || col.Name == "deleted_at") {
|
||||||
|
continue // Soft-delete state is managed by SoftDeleteXyz(), not by updates
|
||||||
|
}
|
||||||
|
ret = append(ret, col)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
34
pkg/schema/table_test.go
Normal file
34
pkg/schema/table_test.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestTableGetUpdateableColumns(t *testing.T) {
|
||||||
|
tbl := Table{
|
||||||
|
Columns: []Column{
|
||||||
|
{Name: "rowid"},
|
||||||
|
{Name: "version", Type: "integer", IsPrimaryKey: true},
|
||||||
|
{Name: "created_at", Type: "integer"},
|
||||||
|
{Name: "updated_at", Type: "integer"},
|
||||||
|
{Name: "is_deleted", Type: "integer"},
|
||||||
|
{Name: "deleted_at", Type: "integer"},
|
||||||
|
{Name: "owner_id", Type: "integer", IsForeignKey: true},
|
||||||
|
{Name: "name", Type: "text"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
gotColumns := tbl.GetUpdateableColumns()
|
||||||
|
got := make([]string, 0, len(gotColumns))
|
||||||
|
for _, col := range gotColumns {
|
||||||
|
got = append(got, col.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := []string{"updated_at", "owner_id", "name"}
|
||||||
|
if len(got) != len(want) {
|
||||||
|
t.Fatalf("GetUpdateableColumns() = %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
for i := range want {
|
||||||
|
if got[i] != want[i] {
|
||||||
|
t.Fatalf("GetUpdateableColumns() = %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user