Skip to content

Commit

Permalink
Delete() 完善 (#96)
Browse files Browse the repository at this point in the history
* Delete的执行

* 改为范型、修改测试用例、添加集成测试

* 添加changelog条目

* 修复 err 未处理

* 存在测试的问题.

* 修改db_test中的命名.
  • Loading branch information
archer-wyz authored Oct 8, 2022
1 parent b538198 commit 6c7dcac
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 21 deletions.
1 change: 1 addition & 0 deletions .CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
- [eorm, internal/valuer: 摒弃中间表达,直接依赖于 Scan](https://github.com/gotomicro/eorm/pull/79)
- [eorm:修改 ErrNoRows 的语义,只有在 Get 才会返回](https://github.com/gotomicro/eorm/pull/80)
- [all: Field 取代 FieldByName](https://github.com/gotomicro/eorm/pull/90)
- [eorm: Delete的执行完成](https://github.com/gotomicro/eorm/pull/90)

### 文档, 代码质量以及文档
- [Add examples and docs for Aggregate and Assign](https://github.com/gotomicro/eorm/pull/50)
Expand Down
10 changes: 0 additions & 10 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,6 @@ func (db *DB) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
return &Tx{tx: tx, db: db}, nil
}

// Delete 开始构建一个 DELETE 查询
func (db *DB) Delete() *Deleter {
return &Deleter{
builder: builder{
core: db.core,
buffer: bytebufferpool.Get(),
},
}
}

// Update 开始构建一个 UPDATE 查询
func (db *DB) Update(table interface{}) *Updater {
return &Updater{
Expand Down
4 changes: 2 additions & 2 deletions db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,10 @@ func ExampleOpen() {
// case1 dialect: SQLite
}

func ExampleDB_Delete() {
func ExampleNewDeleter() {
db := memoryDB()
tm := &TestModel{}
query, _ := db.Delete().From(tm).Build()
query, _ := NewDeleter[TestModel](db).From(tm).Build()
fmt.Printf("SQL: %s", query.SQL)
// Output:
// SQL: DELETE FROM `test_model`;
Expand Down
38 changes: 34 additions & 4 deletions delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,39 @@

package eorm

import (
"context"
"database/sql"

"github.com/valyala/bytebufferpool"
)

// Deleter builds DELETE query
type Deleter struct {
type Deleter[T any] struct {
builder
session
table interface{}
where []Predicate
}

// NewDeleter 开始构建一个 DELETE 查询
func NewDeleter[T any](sess session) *Deleter[T] {
return &Deleter[T]{
builder: builder{
core: sess.getCore(),
buffer: bytebufferpool.Get(),
},
session: sess,
}
}

// Build returns DELETE query
func (d *Deleter) Build() (*Query, error) {
func (d *Deleter[T]) Build() (*Query, error) {
_, _ = d.buffer.WriteString("DELETE FROM ")
var err error
if d.table == nil {
d.table = new(T)
}
d.meta, err = d.metaRegistry.Get(d.table)
if err != nil {
return nil, err
Expand All @@ -43,13 +65,21 @@ func (d *Deleter) Build() (*Query, error) {
}

// From accepts model definition
func (d *Deleter) From(table interface{}) *Deleter {
func (d *Deleter[T]) From(table interface{}) *Deleter[T] {
d.table = table
return d
}

// Where accepts predicates
func (d *Deleter) Where(predicates ...Predicate) *Deleter {
func (d *Deleter[T]) Where(predicates ...Predicate) *Deleter[T] {
d.where = predicates
return d
}

func (d *Deleter[T]) Exec(ctx context.Context) (sql.Result, error) {
query, err := d.Build()
if err != nil {
return nil, err
}
return newQuerier[T](d.session, query).Exec(ctx)
}
96 changes: 91 additions & 5 deletions delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,28 @@
package eorm

import (
"context"
"database/sql"
"errors"
"fmt"
"testing"

"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/require"

"github.com/stretchr/testify/assert"
)

func TestDeleter_Build(t *testing.T) {
testCases := []CommonTestCase{
{
name: "no where",
builder: memoryDB().Delete().From(&TestModel{}),
builder: NewDeleter[TestModel](memoryDB()).From(&TestModel{}),
wantSql: "DELETE FROM `test_model`;",
},
{
name: "where",
builder: memoryDB().Delete().From(&TestModel{}).Where(C("Id").EQ(16)),
builder: NewDeleter[TestModel](memoryDB()).Where(C("Id").EQ(16)),
wantSql: "DELETE FROM `test_model` WHERE `id`=?;",
wantArgs: []interface{}{16},
},
Expand All @@ -47,22 +53,102 @@ func TestDeleter_Build(t *testing.T) {
}
}

func TestDeleter_Exec(t *testing.T) {

testCases := []struct {
name string
mockOrder func(mock sqlmock.Sqlmock)
delete func(*DB, *testing.T) (sql.Result, error)
wantErr error
wantVal sql.Result
}{
{
name: "直接删除",
mockOrder: func(mock sqlmock.Sqlmock) {
mock.ExpectExec("DELETE FROM `test_model` WHERE `id`=").WithArgs(1).WillReturnResult(sqlmock.NewResult(100, 1000))
},
delete: func(db *DB, t *testing.T) (sql.Result, error) {
deleter := NewDeleter[TestModel](db)
result, err := deleter.From(&TestModel{}).Where(C("Id").EQ(1)).Exec(context.Background())
return result, err
},
wantErr: nil,
wantVal: sqlmock.NewResult(100, 1000),
},
{
name: "事务删除",
mockOrder: func(mock sqlmock.Sqlmock) {
mock.ExpectBegin()
mock.ExpectExec("DELETE FROM `test_model` WHERE `id`=").WithArgs(1).WillReturnResult(sqlmock.NewResult(10, 20))
mock.ExpectCommit().WillReturnError(errors.New("commit 错误"))
},
delete: func(db *DB, t *testing.T) (sql.Result, error) {
tx, err := db.BeginTx(context.Background(), &sql.TxOptions{})
require.NoError(t, err)

deleter := NewDeleter[TestModel](db)
result, err := deleter.From(&TestModel{}).Where(C("Id").EQ(1)).Exec(context.Background())
require.NoError(t, err)

err = tx.Commit()
return result, err
},
wantErr: errors.New("commit 错误"),
wantVal: sqlmock.NewResult(10, 20),
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mockDB, mock, err := sqlmock.New()
if err != nil {
t.Fatal(err)
}
db, err := openDB("mysql", mockDB)
defer func(db *DB) { _ = db.Close() }(db)
if err != nil {
t.Fatal(err)
}
tc.mockOrder(mock)
result, err := tc.delete(db, t)

assert.Equal(t, tc.wantErr, err)

rowsAffectedExpect, err := tc.wantVal.RowsAffected()
require.NoError(t, err)
rowsAffected, err := result.RowsAffected()
require.NoError(t, err)
assert.Equal(t, rowsAffectedExpect, rowsAffected)

lastInsertIdExpected, err := tc.wantVal.LastInsertId()
require.NoError(t, err)
lastInsertId, err := result.LastInsertId()
require.NoError(t, err)
assert.Equal(t, lastInsertIdExpected, lastInsertId)

if err = mock.ExpectationsWereMet(); err != nil {
t.Error(err)
}
})
}
}

func ExampleDeleter_Build() {
query, _ := memoryDB().Delete().From(&TestModel{}).Build()
query, _ := NewDeleter[TestModel](memoryDB()).From(&TestModel{}).Build()
fmt.Printf("SQL: %s", query.SQL)
// Output:
// SQL: DELETE FROM `test_model`;
}

func ExampleDeleter_From() {
query, _ := memoryDB().Delete().From(&TestModel{}).Build()
query, _ := NewDeleter[TestModel](memoryDB()).From(&TestModel{}).Build()
fmt.Printf("SQL: %s", query.SQL)
// Output:
// SQL: DELETE FROM `test_model`;
}

func ExampleDeleter_Where() {
query, _ := memoryDB().Delete().From(&TestModel{}).Where(C("Id").EQ(12)).Build()
query, _ := NewDeleter[TestModel](memoryDB()).Where(C("Id").EQ(12)).Build()
fmt.Printf("SQL: %s\nArgs: %v", query.SQL, query.Args)
// Output:
// SQL: DELETE FROM `test_model` WHERE `id`=?;
Expand Down
89 changes: 89 additions & 0 deletions internal/integration/delete_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2021 gotomicro
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build e2e

package integration

import (
"context"
"testing"

_ "github.com/go-sql-driver/mysql"
"github.com/gotomicro/eorm"
"github.com/gotomicro/eorm/internal/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)

type DeleteTestSuite struct {
Suite
}

func (s *DeleteTestSuite) SetupSuite() {
s.Suite.SetupSuite()
data1 := test.NewSimpleStruct(1)
data2 := test.NewSimpleStruct(2)
data3 := test.NewSimpleStruct(3)
_, err := eorm.NewInserter[test.SimpleStruct](s.orm).Values(data1, data2, data3).Exec(context.Background())
if err != nil {
s.T().Fatal(err)
}
}

func (i *DeleteTestSuite) TearDownTest() {
_, err := eorm.RawQuery[any](i.orm, "DELETE FROM `simple_struct`").Exec(context.Background())
if err != nil {
i.T().Fatal(err)
}
}

func (i *DeleteTestSuite) TestDeleter() {
testCases := []struct {
name string
i *eorm.Deleter[test.SimpleStruct]
rowsAffected int64
wantErr error
}{
{
name: "id only",
i: eorm.NewDeleter[test.SimpleStruct](i.orm).From(&test.SimpleStruct{}).Where(eorm.C("Id").EQ("1")),
rowsAffected: 1,
},
{
name: "all field",
i: eorm.NewDeleter[test.SimpleStruct](i.orm).From(&test.SimpleStruct{}),
rowsAffected: 2,
},
}
for _, tc := range testCases {
i.T().Run(tc.name, func(t *testing.T) {
res, err := tc.i.Exec(context.Background())
require.Equal(t, tc.wantErr, err)
affected, err := res.RowsAffected()
require.NotNil(t, err)
assert.Equal(t, tc.rowsAffected, affected)
})
}
}

func TestMySQL8tDelete(t *testing.T) {
suite.Run(t, &DeleteTestSuite{
Suite{
driver: "mysql",
dsn: "root:root@tcp(localhost:13306)/integration_test",
},
})
}

0 comments on commit 6c7dcac

Please sign in to comment.