1
0
mirror of https://github.com/zhashkevych/go-sqlxmock.git synced 2025-06-12 21:47:29 +02:00

implements named arguments support and adds delay expectation for context deadline simulation

This commit is contained in:
gedi
2017-02-06 14:38:57 +02:00
parent 55ecc5a333
commit d11f623794
4 changed files with 191 additions and 24 deletions

View File

@ -7,6 +7,7 @@ import (
"regexp"
"strings"
"sync"
"time"
)
// an expectation interface
@ -54,6 +55,7 @@ func (e *ExpectedClose) String() string {
// returned by *Sqlmock.ExpectBegin.
type ExpectedBegin struct {
commonExpectation
delay time.Duration
}
// WillReturnError allows to set an error for *sql.DB.Begin action
@ -71,6 +73,13 @@ func (e *ExpectedBegin) String() string {
return msg
}
// WillDelayFor allows to specify duration for which it will delay
// result. May be used together with Context
func (e *ExpectedBegin) WillDelayFor(duration time.Duration) *ExpectedBegin {
e.delay = duration
return e
}
// ExpectedCommit is used to manage *sql.Tx.Commit expectation
// returned by *Sqlmock.ExpectCommit.
type ExpectedCommit struct {
@ -118,7 +127,8 @@ func (e *ExpectedRollback) String() string {
// Returned by *Sqlmock.ExpectQuery.
type ExpectedQuery struct {
queryBasedExpectation
rows driver.Rows
rows driver.Rows
delay time.Duration
}
// WithArgs will match given expected args to actual database query arguments.
@ -142,6 +152,13 @@ func (e *ExpectedQuery) WillReturnRows(rows driver.Rows) *ExpectedQuery {
return e
}
// WillDelayFor allows to specify duration for which it will delay
// result. May be used together with Context
func (e *ExpectedQuery) WillDelayFor(duration time.Duration) *ExpectedQuery {
e.delay = duration
return e
}
// String returns string representation
func (e *ExpectedQuery) String() string {
msg := "ExpectedQuery => expecting Query or QueryRow which:"
@ -178,6 +195,7 @@ func (e *ExpectedQuery) String() string {
type ExpectedExec struct {
queryBasedExpectation
result driver.Result
delay time.Duration
}
// WithArgs will match given expected args to actual database exec operation arguments.
@ -194,6 +212,13 @@ func (e *ExpectedExec) WillReturnError(err error) *ExpectedExec {
return e
}
// WillDelayFor allows to specify duration for which it will delay
// result. May be used together with Context
func (e *ExpectedExec) WillDelayFor(duration time.Duration) *ExpectedExec {
e.delay = duration
return e
}
// String returns string representation
func (e *ExpectedExec) String() string {
msg := "ExpectedExec => expecting Exec which:"
@ -244,6 +269,7 @@ type ExpectedPrepare struct {
sqlRegex *regexp.Regexp
statement driver.Stmt
closeErr error
delay time.Duration
}
// WillReturnError allows to set an error for the expected *sql.DB.Prepare or *sql.Tx.Prepare action.
@ -258,6 +284,13 @@ func (e *ExpectedPrepare) WillReturnCloseError(err error) *ExpectedPrepare {
return e
}
// WillDelayFor allows to specify duration for which it will delay
// result. May be used together with Context
func (e *ExpectedPrepare) WillDelayFor(duration time.Duration) *ExpectedPrepare {
e.delay = duration
return e
}
// ExpectQuery allows to expect Query() or QueryRow() on this prepared statement.
// this method is convenient in order to prevent duplicating sql query string matching.
func (e *ExpectedPrepare) ExpectQuery() *ExpectedQuery {
@ -300,7 +333,7 @@ type queryBasedExpectation struct {
args []driver.Value
}
func (e *queryBasedExpectation) attemptMatch(sql string, args []driver.Value) (err error) {
func (e *queryBasedExpectation) attemptMatch(sql string, args []namedValue) (err error) {
if !e.queryMatches(sql) {
return fmt.Errorf(`could not match sql: "%s" with expected regexp "%s"`, sql, e.sqlRegex.String())
}
@ -323,7 +356,7 @@ func (e *queryBasedExpectation) queryMatches(sql string) bool {
return e.sqlRegex.MatchString(sql)
}
func (e *queryBasedExpectation) argsMatches(args []driver.Value) error {
func (e *queryBasedExpectation) argsMatches(args []namedValue) error {
if nil == e.args {
return nil
}
@ -334,14 +367,26 @@ func (e *queryBasedExpectation) argsMatches(args []driver.Value) error {
// custom argument matcher
matcher, ok := e.args[k].(Argument)
if ok {
if !matcher.Match(v) {
// @TODO: does it make sense to pass value instead of named value?
if !matcher.Match(v.Value) {
return fmt.Errorf("matcher %T could not match %d argument %T - %+v", matcher, k, args[k], args[k])
}
continue
}
dval := e.args[k]
if named, isNamed := dval.(namedValue); isNamed {
dval = named.Value
if v.Name != named.Name {
return fmt.Errorf("named argument %d: name: \"%s\" does not match expected: \"%s\"", k, v.Name, named.Name)
}
if v.Ordinal != named.Ordinal {
return fmt.Errorf("named argument %d: ordinal position: \"%d\" does not match expected: \"%d\"", k, v.Ordinal, named.Ordinal)
}
}
// convert to driver converter
darg, err := driver.DefaultParameterConverter.ConvertValue(e.args[k])
darg, err := driver.DefaultParameterConverter.ConvertValue(dval)
if err != nil {
return fmt.Errorf("could not convert %d argument %T - %+v to driver value: %s", k, e.args[k], e.args[k], err)
}
@ -350,8 +395,8 @@ func (e *queryBasedExpectation) argsMatches(args []driver.Value) error {
return fmt.Errorf("argument %d: non-subset type %T returned from Value", k, darg)
}
if !reflect.DeepEqual(darg, args[k]) {
return fmt.Errorf("argument %d expected [%T - %+v] does not match actual [%T - %+v]", k, darg, darg, args[k], args[k])
if !reflect.DeepEqual(darg, v.Value) {
return fmt.Errorf("argument %d expected [%T - %+v] does not match actual [%T - %+v]", k, darg, darg, v.Value, v.Value)
}
}
return nil