Create comprehensive documentation for future Claude Code instances working in this repository, including: - Development commands for testing, building, and code quality - Core architecture overview of the SQL query builder system - Directory structure and component explanations - Testing patterns and conventions - Key dependencies and their purposes 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
512 lines
14 KiB
Go
512 lines
14 KiB
Go
package db_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
dbv2 "git.fsdpf.net/go/db"
|
|
"git.fsdpf.net/go/db/exp"
|
|
"git.fsdpf.net/go/db/internal/errors"
|
|
"git.fsdpf.net/go/db/internal/sb"
|
|
"git.fsdpf.net/go/db/mocks"
|
|
"github.com/DATA-DOG/go-sqlmock"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/suite"
|
|
)
|
|
|
|
type (
|
|
deleteTestCase struct {
|
|
ds *dbv2.DeleteDataset
|
|
clauses exp.DeleteClauses
|
|
}
|
|
deleteDatasetSuite struct {
|
|
suite.Suite
|
|
}
|
|
)
|
|
|
|
func (dds *deleteDatasetSuite) assertCases(cases ...deleteTestCase) {
|
|
for _, s := range cases {
|
|
dds.Equal(s.clauses, s.ds.GetClauses())
|
|
}
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) SetupSuite() {
|
|
noReturn := dbv2.DefaultDialectOptions()
|
|
noReturn.SupportsReturn = false
|
|
dbv2.RegisterDialect("no-return", noReturn)
|
|
|
|
limitOnDelete := dbv2.DefaultDialectOptions()
|
|
limitOnDelete.SupportsLimitOnDelete = true
|
|
dbv2.RegisterDialect("limit-on-delete", limitOnDelete)
|
|
|
|
orderOnDelete := dbv2.DefaultDialectOptions()
|
|
orderOnDelete.SupportsOrderByOnDelete = true
|
|
dbv2.RegisterDialect("order-on-delete", orderOnDelete)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TearDownSuite() {
|
|
dbv2.DeregisterDialect("no-return")
|
|
dbv2.DeregisterDialect("limit-on-delete")
|
|
dbv2.DeregisterDialect("order-on-delete")
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestDelete() {
|
|
ds := dbv2.Delete("test")
|
|
dds.IsType(&dbv2.DeleteDataset{}, ds)
|
|
dds.Implements((*exp.Expression)(nil), ds)
|
|
dds.Implements((*exp.AppendableExpression)(nil), ds)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestClone() {
|
|
ds := dbv2.Delete("test")
|
|
dds.Equal(ds.Clone(), ds)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestExpression() {
|
|
ds := dbv2.Delete("test")
|
|
dds.Equal(ds.Expression(), ds)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestDialect() {
|
|
ds := dbv2.Delete("test")
|
|
dds.NotNil(ds.Dialect())
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestWithDialect() {
|
|
ds := dbv2.Delete("test")
|
|
md := new(mocks.SQLDialect)
|
|
ds = ds.SetDialect(md)
|
|
|
|
dialect := dbv2.GetDialect("default")
|
|
dialectDs := ds.WithDialect("default")
|
|
dds.Equal(md, ds.Dialect())
|
|
dds.Equal(dialect, dialectDs.Dialect())
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestPrepared() {
|
|
ds := dbv2.Delete("test")
|
|
preparedDs := ds.Prepared(true)
|
|
dds.True(preparedDs.IsPrepared())
|
|
dds.False(ds.IsPrepared())
|
|
// should apply the prepared to any datasets created from the root
|
|
dds.True(preparedDs.Where(dbv2.Ex{"a": 1}).IsPrepared())
|
|
|
|
defer dbv2.SetDefaultPrepared(false)
|
|
dbv2.SetDefaultPrepared(true)
|
|
|
|
// should be prepared by default
|
|
ds = dbv2.Delete("test")
|
|
dds.True(ds.IsPrepared())
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestGetClauses() {
|
|
ds := dbv2.Delete("test")
|
|
ce := exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test"))
|
|
dds.Equal(ce, ds.GetClauses())
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestWith() {
|
|
from := dbv2.From("cte")
|
|
bd := dbv2.Delete("items")
|
|
dds.assertCases(
|
|
deleteTestCase{
|
|
ds: bd.With("test-cte", from),
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")).
|
|
CommonTablesAppend(exp.NewCommonTableExpression(false, "test-cte", from)),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd,
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")),
|
|
},
|
|
)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestWithRecursive() {
|
|
from := dbv2.From("cte")
|
|
bd := dbv2.Delete("items")
|
|
dds.assertCases(
|
|
deleteTestCase{
|
|
ds: bd.WithRecursive("test-cte", from),
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")).
|
|
CommonTablesAppend(exp.NewCommonTableExpression(true, "test-cte", from)),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd,
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")),
|
|
},
|
|
)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestFrom_withIdentifier() {
|
|
bd := dbv2.Delete("items")
|
|
dds.assertCases(
|
|
deleteTestCase{
|
|
ds: bd.From("items2"),
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items2")),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd.From(dbv2.C("items2")),
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items2")),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd.From(dbv2.T("items2")),
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items2")),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd.From("schema.table"),
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("schema.table")),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd,
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")),
|
|
},
|
|
)
|
|
|
|
dds.PanicsWithValue(dbv2.ErrBadFromArgument, func() {
|
|
dbv2.Delete("test").From(true)
|
|
})
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestWhere() {
|
|
bd := dbv2.Delete("items")
|
|
dds.assertCases(
|
|
deleteTestCase{
|
|
ds: bd.Where(dbv2.Ex{"a": 1}),
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("items")).
|
|
WhereAppend(dbv2.Ex{"a": 1}),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd.Where(dbv2.Ex{"a": 1}).Where(dbv2.C("b").Eq("c")),
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("items")).
|
|
WhereAppend(dbv2.Ex{"a": 1}).
|
|
WhereAppend(dbv2.C("b").Eq("c")),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd,
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")),
|
|
},
|
|
)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestClearWhere() {
|
|
bd := dbv2.Delete("items").Where(dbv2.Ex{"a": 1})
|
|
dds.assertCases(
|
|
deleteTestCase{
|
|
ds: bd.ClearWhere(),
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("items")),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd,
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("items")).
|
|
WhereAppend(dbv2.Ex{"a": 1}),
|
|
},
|
|
)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestOrder() {
|
|
bd := dbv2.Delete("items")
|
|
dds.assertCases(
|
|
deleteTestCase{
|
|
ds: bd.Order(dbv2.C("a").Asc()),
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("items")).
|
|
SetOrder(dbv2.C("a").Asc()),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd.Order(dbv2.C("a").Asc()).Order(dbv2.C("b").Desc()),
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("items")).
|
|
SetOrder(dbv2.C("b").Desc()),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd.Order(dbv2.C("a").Asc(), dbv2.C("b").Desc()),
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("items")).
|
|
SetOrder(dbv2.C("a").Asc(), dbv2.C("b").Desc()),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd,
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")),
|
|
},
|
|
)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestOrderAppend() {
|
|
bd := dbv2.Delete("items").Order(dbv2.C("a").Asc())
|
|
dds.assertCases(
|
|
deleteTestCase{
|
|
ds: bd.OrderAppend(dbv2.C("b").Desc()),
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("items")).
|
|
SetOrder(dbv2.C("a").Asc(), dbv2.C("b").Desc()),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd,
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("items")).
|
|
SetOrder(dbv2.C("a").Asc()),
|
|
},
|
|
)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestOrderPrepend() {
|
|
bd := dbv2.Delete("items").Order(dbv2.C("a").Asc())
|
|
dds.assertCases(
|
|
deleteTestCase{
|
|
ds: bd.OrderPrepend(dbv2.C("b").Desc()),
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("items")).
|
|
SetOrder(dbv2.C("b").Desc(), dbv2.C("a").Asc()),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd,
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("items")).
|
|
SetOrder(dbv2.C("a").Asc()),
|
|
},
|
|
)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestClearOrder() {
|
|
bd := dbv2.Delete("items").Order(dbv2.C("a").Asc())
|
|
dds.assertCases(
|
|
deleteTestCase{
|
|
ds: bd.ClearOrder(),
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd,
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("items")).
|
|
SetOrder(dbv2.C("a").Asc()),
|
|
},
|
|
)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestLimit() {
|
|
bd := dbv2.Delete("test")
|
|
dds.assertCases(
|
|
deleteTestCase{
|
|
ds: bd.Limit(10),
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("test")).
|
|
SetLimit(uint(10)),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd.Limit(0),
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd.Limit(10).Limit(2),
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("test")).
|
|
SetLimit(uint(2)),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd.Limit(10).Limit(0),
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd,
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")),
|
|
},
|
|
)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestLimitAll() {
|
|
bd := dbv2.Delete("test")
|
|
dds.assertCases(
|
|
deleteTestCase{
|
|
ds: bd.LimitAll(),
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("test")).
|
|
SetLimit(dbv2.L("ALL")),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd.Limit(10).LimitAll(),
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("test")).
|
|
SetLimit(dbv2.L("ALL")),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd,
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")),
|
|
},
|
|
)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestClearLimit() {
|
|
bd := dbv2.Delete("test").Limit(10)
|
|
dds.assertCases(
|
|
deleteTestCase{
|
|
ds: bd.ClearLimit(),
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd,
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("test")).SetLimit(uint(10)),
|
|
},
|
|
)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestReturning() {
|
|
bd := dbv2.Delete("items")
|
|
dds.assertCases(
|
|
deleteTestCase{
|
|
ds: bd.Returning("a"),
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("items")).
|
|
SetReturning(exp.NewColumnListExpression("a")),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd.Returning(),
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("items")).
|
|
SetReturning(exp.NewColumnListExpression()),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd.Returning(nil),
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("items")).
|
|
SetReturning(exp.NewColumnListExpression()),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd.Returning("a").Returning("b"),
|
|
clauses: exp.NewDeleteClauses().
|
|
SetFrom(exp.NewColumnListExpression("items")).
|
|
SetReturning(exp.NewColumnListExpression("b")),
|
|
},
|
|
deleteTestCase{
|
|
ds: bd,
|
|
clauses: exp.NewDeleteClauses().SetFrom(exp.NewColumnListExpression("items")),
|
|
},
|
|
)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestReturnsColumns() {
|
|
ds := dbv2.Delete("test")
|
|
dds.False(ds.ReturnsColumns())
|
|
dds.True(ds.Returning("foo", "bar").ReturnsColumns())
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestToSQL() {
|
|
md := new(mocks.SQLDialect)
|
|
ds := dbv2.Delete("test").SetDialect(md)
|
|
c := ds.GetClauses()
|
|
sqlB := sb.NewSQLBuilder(false)
|
|
md.On("ToDeleteSQL", sqlB, c).Return(nil).Once()
|
|
|
|
sql, args, err := ds.ToSQL()
|
|
dds.Empty(sql)
|
|
dds.Empty(args)
|
|
dds.Nil(err)
|
|
md.AssertExpectations(dds.T())
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestToSQL_Prepared() {
|
|
md := new(mocks.SQLDialect)
|
|
ds := dbv2.Delete("test").Prepared(true).SetDialect(md)
|
|
c := ds.GetClauses()
|
|
sqlB := sb.NewSQLBuilder(true)
|
|
md.On("ToDeleteSQL", sqlB, c).Return(nil).Once()
|
|
|
|
sql, args, err := ds.ToSQL()
|
|
dds.Empty(sql)
|
|
dds.Empty(args)
|
|
dds.Nil(err)
|
|
md.AssertExpectations(dds.T())
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestToSQL_WithError() {
|
|
md := new(mocks.SQLDialect)
|
|
ds := dbv2.Delete("test").SetDialect(md)
|
|
c := ds.GetClauses()
|
|
ee := errors.New("expected error")
|
|
sqlB := sb.NewSQLBuilder(false)
|
|
md.On("ToDeleteSQL", sqlB, c).Run(func(args mock.Arguments) {
|
|
args.Get(0).(sb.SQLBuilder).SetError(ee)
|
|
}).Once()
|
|
|
|
sql, args, err := ds.ToSQL()
|
|
dds.Empty(sql)
|
|
dds.Empty(args)
|
|
dds.Equal(ee, err)
|
|
md.AssertExpectations(dds.T())
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestExecutor() {
|
|
mDB, _, err := sqlmock.New()
|
|
dds.NoError(err)
|
|
|
|
ds := dbv2.New("mock", mDB).Delete("items").Where(dbv2.Ex{"id": dbv2.Op{"gt": 10}})
|
|
|
|
dsql, args, err := ds.Executor().ToSQL()
|
|
dds.NoError(err)
|
|
dds.Empty(args)
|
|
dds.Equal(`DELETE FROM "items" WHERE ("id" > 10)`, dsql)
|
|
|
|
dsql, args, err = ds.Prepared(true).Executor().ToSQL()
|
|
dds.NoError(err)
|
|
dds.Equal([]interface{}{int64(10)}, args)
|
|
dds.Equal(`DELETE FROM "items" WHERE ("id" > ?)`, dsql)
|
|
|
|
defer dbv2.SetDefaultPrepared(false)
|
|
dbv2.SetDefaultPrepared(true)
|
|
|
|
dsql, args, err = ds.Executor().ToSQL()
|
|
dds.NoError(err)
|
|
dds.Equal([]interface{}{int64(10)}, args)
|
|
dds.Equal(`DELETE FROM "items" WHERE ("id" > ?)`, dsql)
|
|
}
|
|
|
|
func (dds *deleteDatasetSuite) TestSetError() {
|
|
err1 := errors.New("error #1")
|
|
err2 := errors.New("error #2")
|
|
err3 := errors.New("error #3")
|
|
|
|
// Verify initial error set/get works properly
|
|
md := new(mocks.SQLDialect)
|
|
ds := dbv2.Delete("test").SetDialect(md)
|
|
ds = ds.SetError(err1)
|
|
dds.Equal(err1, ds.Error())
|
|
sql, args, err := ds.ToSQL()
|
|
dds.Empty(sql)
|
|
dds.Empty(args)
|
|
dds.Equal(err1, err)
|
|
|
|
// Repeated SetError calls on Dataset should not overwrite the original error
|
|
ds = ds.SetError(err2)
|
|
dds.Equal(err1, ds.Error())
|
|
sql, args, err = ds.ToSQL()
|
|
dds.Empty(sql)
|
|
dds.Empty(args)
|
|
dds.Equal(err1, err)
|
|
|
|
// Builder functions should not lose the error
|
|
ds = ds.ClearLimit()
|
|
dds.Equal(err1, ds.Error())
|
|
sql, args, err = ds.ToSQL()
|
|
dds.Empty(sql)
|
|
dds.Empty(args)
|
|
dds.Equal(err1, err)
|
|
|
|
// Deeper errors inside SQL generation should still return original error
|
|
c := ds.GetClauses()
|
|
sqlB := sb.NewSQLBuilder(false)
|
|
md.On("ToDeleteSQL", sqlB, c).Run(func(args mock.Arguments) {
|
|
args.Get(0).(sb.SQLBuilder).SetError(err3)
|
|
}).Once()
|
|
|
|
sql, args, err = ds.ToSQL()
|
|
dds.Empty(sql)
|
|
dds.Empty(args)
|
|
dds.Equal(err1, err)
|
|
}
|
|
|
|
func TestDeleteDataset(t *testing.T) {
|
|
suite.Run(t, new(deleteDatasetSuite))
|
|
}
|