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 ( updateTestCase struct { ds *dbv2.UpdateDataset clauses exp.UpdateClauses } updateDatasetSuite struct { suite.Suite } ) func (uds *updateDatasetSuite) assertCases(cases ...updateTestCase) { for _, s := range cases { uds.Equal(s.clauses, s.ds.GetClauses()) } } func (uds *updateDatasetSuite) TestUpdate() { ds := dbv2.Update("test") uds.IsType(&dbv2.UpdateDataset{}, ds) uds.Implements((*exp.Expression)(nil), ds) uds.Implements((*exp.AppendableExpression)(nil), ds) } func (uds *updateDatasetSuite) TestClone() { ds := dbv2.Update("test") uds.Equal(ds, ds.Clone()) } func (uds *updateDatasetSuite) TestExpression() { ds := dbv2.Update("test") uds.Equal(ds, ds.Expression()) } func (uds *updateDatasetSuite) TestDialect() { ds := dbv2.Update("test") uds.NotNil(ds.Dialect()) } func (uds *updateDatasetSuite) TestWithDialect() { ds := dbv2.Update("test") md := new(mocks.SQLDialect) ds = ds.SetDialect(md) dialect := dbv2.GetDialect("default") dialectDs := ds.WithDialect("default") uds.Equal(md, ds.Dialect()) uds.Equal(dialect, dialectDs.Dialect()) } func (uds *updateDatasetSuite) TestPrepared() { ds := dbv2.Update("test") preparedDs := ds.Prepared(true) uds.True(preparedDs.IsPrepared()) uds.False(ds.IsPrepared()) // should apply the prepared to any datasets created from the root uds.True(preparedDs.Where(dbv2.Ex{"a": 1}).IsPrepared()) defer dbv2.SetDefaultPrepared(false) dbv2.SetDefaultPrepared(true) // should be prepared by default ds = dbv2.Update("test") uds.True(ds.IsPrepared()) } func (uds *updateDatasetSuite) TestGetClauses() { ds := dbv2.Update("test") ce := exp.NewUpdateClauses().SetTable(dbv2.I("test")) uds.Equal(ce, ds.GetClauses()) } func (uds *updateDatasetSuite) TestWith() { from := dbv2.Update("cte") bd := dbv2.Update("items") uds.assertCases( updateTestCase{ ds: bd.With("test-cte", from), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). CommonTablesAppend(exp.NewCommonTableExpression(false, "test-cte", from)), }, updateTestCase{ ds: bd, clauses: exp.NewUpdateClauses().SetTable(dbv2.C("items")), }, ) } func (uds *updateDatasetSuite) TestWithRecursive() { from := dbv2.Update("cte") bd := dbv2.Update("items") uds.assertCases( updateTestCase{ ds: bd.WithRecursive("test-cte", from), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). CommonTablesAppend(exp.NewCommonTableExpression(true, "test-cte", from)), }, updateTestCase{ ds: bd, clauses: exp.NewUpdateClauses().SetTable(dbv2.C("items")), }, ) } func (uds *updateDatasetSuite) TestTable() { bd := dbv2.Update("items") uds.assertCases( updateTestCase{ ds: bd.Table("items2"), clauses: exp.NewUpdateClauses().SetTable(dbv2.C("items2")), }, updateTestCase{ ds: bd.Table(dbv2.L("literal_table")), clauses: exp.NewUpdateClauses().SetTable(dbv2.L("literal_table")), }, updateTestCase{ ds: bd, clauses: exp.NewUpdateClauses().SetTable(dbv2.C("items")), }, ) uds.PanicsWithValue(dbv2.ErrUnsupportedUpdateTableType, func() { bd.Table(true) }) } func (uds *updateDatasetSuite) TestSet() { type item struct { Address string `db:"address"` Name string `db:"name"` } bd := dbv2.Update("items") uds.assertCases( updateTestCase{ ds: bd.Set(item{Name: "Test", Address: "111 Test Addr"}), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). SetSetValues(item{Name: "Test", Address: "111 Test Addr"}), }, updateTestCase{ ds: bd.Set(dbv2.Record{"name": "Test", "address": "111 Test Addr"}), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). SetSetValues(dbv2.Record{"name": "Test", "address": "111 Test Addr"}), }, updateTestCase{ ds: bd, clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")), }, updateTestCase{ ds: bd.Set([]exp.UpdateExpression{ dbv2.C("name").Set("Test"), dbv2.C("address").Set("111 Test Addr"), }), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). SetSetValues([]exp.UpdateExpression{ dbv2.C("name").Set("Test"), dbv2.C("address").Set("111 Test Addr"), }), }, ) } func (uds *updateDatasetSuite) TestFrom() { bd := dbv2.Update("items") uds.assertCases( updateTestCase{ ds: bd.From("other"), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("other")), }, updateTestCase{ ds: bd.From("other").From("other2"), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). SetFrom(exp.NewColumnListExpression("other2")), }, updateTestCase{ ds: bd, clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")), }, ) } func (uds *updateDatasetSuite) TestWhere() { bd := dbv2.Update("items") uds.assertCases( updateTestCase{ ds: bd.Where(dbv2.Ex{"a": 1}), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). WhereAppend(dbv2.Ex{"a": 1}), }, updateTestCase{ ds: bd.Where(dbv2.Ex{"a": 1}).Where(dbv2.C("b").Eq("c")), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). WhereAppend(dbv2.Ex{"a": 1}).WhereAppend(dbv2.C("b").Eq("c")), }, updateTestCase{ ds: bd, clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")), }, ) } func (uds *updateDatasetSuite) TestClearWhere() { bd := dbv2.Update("items").Where(dbv2.Ex{"a": 1}) uds.assertCases( updateTestCase{ ds: bd.ClearWhere(), clauses: exp.NewUpdateClauses().SetTable(dbv2.C("items")), }, updateTestCase{ ds: bd, clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). WhereAppend(dbv2.Ex{"a": 1}), }, ) } func (uds *updateDatasetSuite) TestOrder() { bd := dbv2.Update("items") uds.assertCases( updateTestCase{ ds: bd.Order(dbv2.C("a").Desc()), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")).OrderAppend(dbv2.C("a").Desc()), }, updateTestCase{ ds: bd.Order(dbv2.C("a").Desc()).Order(dbv2.C("b").Asc()), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). OrderAppend(dbv2.C("b").Asc()), }, updateTestCase{ ds: bd, clauses: exp.NewUpdateClauses().SetTable(dbv2.C("items")), }, ) } func (uds *updateDatasetSuite) TestOrderAppend() { bd := dbv2.Update("items").Order(dbv2.C("a").Desc()) uds.assertCases( updateTestCase{ ds: bd.OrderAppend(dbv2.C("b").Asc()), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). OrderAppend(dbv2.C("a").Desc()). OrderAppend(dbv2.C("b").Asc()), }, updateTestCase{ ds: bd, clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). OrderAppend(dbv2.C("a").Desc()), }, ) } func (uds *updateDatasetSuite) TestOrderPrepend() { bd := dbv2.Update("items").Order(dbv2.C("a").Desc()) uds.assertCases( updateTestCase{ ds: bd.OrderPrepend(dbv2.C("b").Asc()), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). OrderAppend(dbv2.C("b").Asc()). OrderAppend(dbv2.C("a").Desc()), }, updateTestCase{ ds: bd, clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). OrderAppend(dbv2.C("a").Desc()), }, ) } func (uds *updateDatasetSuite) TestClearOrder() { bd := dbv2.Update("items").Order(dbv2.C("a").Desc()) uds.assertCases( updateTestCase{ ds: bd.ClearOrder(), clauses: exp.NewUpdateClauses().SetTable(dbv2.C("items")), }, updateTestCase{ ds: bd, clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). OrderAppend(dbv2.C("a").Desc()), }, ) } func (uds *updateDatasetSuite) TestLimit() { bd := dbv2.Update("items") uds.assertCases( updateTestCase{ ds: bd.Limit(10), clauses: exp.NewUpdateClauses().SetTable(dbv2.C("items")).SetLimit(uint(10)), }, updateTestCase{ ds: bd.Limit(0), clauses: exp.NewUpdateClauses().SetTable(dbv2.C("items")), }, updateTestCase{ ds: bd, clauses: exp.NewUpdateClauses().SetTable(dbv2.C("items")), }, ) } func (uds *updateDatasetSuite) TestLimitAll() { bd := dbv2.Update("items") uds.assertCases( updateTestCase{ ds: bd.LimitAll(), clauses: exp.NewUpdateClauses().SetTable(dbv2.C("items")).SetLimit(dbv2.L("ALL")), }, updateTestCase{ ds: bd, clauses: exp.NewUpdateClauses().SetTable(dbv2.C("items")), }, ) } func (uds *updateDatasetSuite) TestClearLimit() { bd := dbv2.Update("items") uds.assertCases( updateTestCase{ ds: bd.LimitAll().ClearLimit(), clauses: exp.NewUpdateClauses().SetTable(dbv2.C("items")), }, updateTestCase{ ds: bd.Limit(10).ClearLimit(), clauses: exp.NewUpdateClauses().SetTable(dbv2.C("items")), }, updateTestCase{ ds: bd, clauses: exp.NewUpdateClauses().SetTable(dbv2.C("items")), }, ) } func (uds *updateDatasetSuite) TestReturning() { bd := dbv2.Update("items") uds.assertCases( updateTestCase{ ds: bd.Returning("a", "b"), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). SetReturning(exp.NewColumnListExpression("a", "b")), }, updateTestCase{ ds: bd.Returning(), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). SetReturning(exp.NewColumnListExpression()), }, updateTestCase{ ds: bd.Returning(nil), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). SetReturning(exp.NewColumnListExpression()), }, updateTestCase{ ds: bd.Returning("a", "b").Returning("c"), clauses: exp.NewUpdateClauses(). SetTable(dbv2.C("items")). SetReturning(exp.NewColumnListExpression("c")), }, updateTestCase{ ds: bd, clauses: exp.NewUpdateClauses().SetTable(dbv2.C("items")), }, ) } func (uds *updateDatasetSuite) TestReturnsColumns() { ds := dbv2.Update("test") uds.False(ds.ReturnsColumns()) uds.True(ds.Returning("foo", "bar").ReturnsColumns()) } func (uds *updateDatasetSuite) TestToSQL() { md := new(mocks.SQLDialect) ds := dbv2.Update("test").SetDialect(md) r := dbv2.Record{"c": "a"} c := ds.GetClauses().SetSetValues(r) sqlB := sb.NewSQLBuilder(false) md.On("ToUpdateSQL", sqlB, c).Return(nil).Once() updateSQL, args, err := ds.Set(r).ToSQL() uds.Empty(updateSQL) uds.Empty(args) uds.Nil(err) md.AssertExpectations(uds.T()) } func (uds *updateDatasetSuite) TestToSQL_Prepared() { md := new(mocks.SQLDialect) ds := dbv2.Update("test").Prepared(true).SetDialect(md) r := dbv2.Record{"c": "a"} c := ds.GetClauses().SetSetValues(r) sqlB := sb.NewSQLBuilder(true) md.On("ToUpdateSQL", sqlB, c).Return(nil).Once() updateSQL, args, err := ds.Set(dbv2.Record{"c": "a"}).ToSQL() uds.Empty(updateSQL) uds.Empty(args) uds.Nil(err) md.AssertExpectations(uds.T()) } func (uds *updateDatasetSuite) TestToSQL_WithError() { md := new(mocks.SQLDialect) ds := dbv2.Update("test").SetDialect(md) r := dbv2.Record{"c": "a"} c := ds.GetClauses().SetSetValues(r) sqlB := sb.NewSQLBuilder(false) ee := errors.New("expected error") md.On("ToUpdateSQL", sqlB, c).Run(func(args mock.Arguments) { args.Get(0).(sb.SQLBuilder).SetError(ee) }).Once() updateSQL, args, err := ds.Set(dbv2.Record{"c": "a"}).ToSQL() uds.Empty(updateSQL) uds.Empty(args) uds.Equal(ee, err) md.AssertExpectations(uds.T()) } func (uds *updateDatasetSuite) TestExecutor() { mDB, _, err := sqlmock.New() uds.NoError(err) ds := dbv2.New("mock", mDB). Update("items"). Set(dbv2.Record{"address": "111 Test Addr", "name": "Test1"}). Where(dbv2.C("name").IsNull()) updateSQL, args, err := ds.Executor().ToSQL() uds.NoError(err) uds.Empty(args) uds.Equal(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE ("name" IS NULL)`, updateSQL) updateSQL, args, err = ds.Prepared(true).Executor().ToSQL() uds.NoError(err) uds.Equal([]interface{}{"111 Test Addr", "Test1"}, args) uds.Equal(`UPDATE "items" SET "address"=?,"name"=? WHERE ("name" IS NULL)`, updateSQL) defer dbv2.SetDefaultPrepared(false) dbv2.SetDefaultPrepared(true) updateSQL, args, err = ds.Executor().ToSQL() uds.NoError(err) uds.Equal([]interface{}{"111 Test Addr", "Test1"}, args) uds.Equal(`UPDATE "items" SET "address"=?,"name"=? WHERE ("name" IS NULL)`, updateSQL) } func (uds *updateDatasetSuite) 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.Update("test").SetDialect(md) ds = ds.SetError(err1) uds.Equal(err1, ds.Error()) sql, args, err := ds.ToSQL() uds.Empty(sql) uds.Empty(args) uds.Equal(err1, err) // Repeated SetError calls on Dataset should not overwrite the original error ds = ds.SetError(err2) uds.Equal(err1, ds.Error()) sql, args, err = ds.ToSQL() uds.Empty(sql) uds.Empty(args) uds.Equal(err1, err) // Builder functions should not lose the error ds = ds.ClearLimit() uds.Equal(err1, ds.Error()) sql, args, err = ds.ToSQL() uds.Empty(sql) uds.Empty(args) uds.Equal(err1, err) // Deeper errors inside SQL generation should still return original error c := ds.GetClauses() sqlB := sb.NewSQLBuilder(false) md.On("ToUpdateSQL", sqlB, c).Run(func(args mock.Arguments) { args.Get(0).(sb.SQLBuilder).SetError(err3) }).Once() sql, args, err = ds.ToSQL() uds.Empty(sql) uds.Empty(args) uds.Equal(err1, err) } func TestUpdateDataset(t *testing.T) { suite.Run(t, new(updateDatasetSuite)) }