mirror of
https://github.com/DATA-DOG/go-sqlmock.git
synced 2024-11-24 08:32:36 +02:00
642 lines
16 KiB
Go
642 lines
16 KiB
Go
// +build go1.8
|
|
|
|
package sqlmock
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestContextExecCancel(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
mock.ExpectExec("DELETE FROM users").
|
|
WillDelayFor(time.Second).
|
|
WillReturnResult(NewResult(1, 1))
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
go func() {
|
|
time.Sleep(time.Millisecond * 10)
|
|
cancel()
|
|
}()
|
|
|
|
_, err = db.ExecContext(ctx, "DELETE FROM users")
|
|
if err == nil {
|
|
t.Error("error was expected, but there was none")
|
|
}
|
|
|
|
if err != ErrCancelled {
|
|
t.Errorf("was expecting cancel error, but got: %v", err)
|
|
}
|
|
|
|
_, err = db.ExecContext(ctx, "DELETE FROM users")
|
|
if err != context.Canceled {
|
|
t.Error("error was expected since context was already done, but there was none")
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Errorf("there were unfulfilled expectations: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestPreparedStatementContextExecCancel(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
mock.ExpectPrepare("DELETE FROM users").
|
|
ExpectExec().
|
|
WillDelayFor(time.Second).
|
|
WillReturnResult(NewResult(1, 1))
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
go func() {
|
|
time.Sleep(time.Millisecond * 10)
|
|
cancel()
|
|
}()
|
|
|
|
stmt, err := db.Prepare("DELETE FROM users")
|
|
if err != nil {
|
|
t.Errorf("error was not expected, but got: %v", err)
|
|
}
|
|
|
|
_, err = stmt.ExecContext(ctx)
|
|
if err == nil {
|
|
t.Error("error was expected, but there was none")
|
|
}
|
|
|
|
if err != ErrCancelled {
|
|
t.Errorf("was expecting cancel error, but got: %v", err)
|
|
}
|
|
|
|
_, err = stmt.ExecContext(ctx)
|
|
if err != context.Canceled {
|
|
t.Error("error was expected since context was already done, but there was none")
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Errorf("there were unfulfilled expectations: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestContextExecWithNamedArg(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
mock.ExpectExec("DELETE FROM users").
|
|
WithArgs(sql.Named("id", 5)).
|
|
WillDelayFor(time.Second).
|
|
WillReturnResult(NewResult(1, 1))
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
go func() {
|
|
time.Sleep(time.Millisecond * 10)
|
|
cancel()
|
|
}()
|
|
|
|
_, err = db.ExecContext(ctx, "DELETE FROM users WHERE id = :id", sql.Named("id", 5))
|
|
if err == nil {
|
|
t.Error("error was expected, but there was none")
|
|
}
|
|
|
|
if err != ErrCancelled {
|
|
t.Errorf("was expecting cancel error, but got: %v", err)
|
|
}
|
|
|
|
_, err = db.ExecContext(ctx, "DELETE FROM users WHERE id = :id", sql.Named("id", 5))
|
|
if err != context.Canceled {
|
|
t.Error("error was expected since context was already done, but there was none")
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Errorf("there were unfulfilled expectations: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestContextExec(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
mock.ExpectExec("DELETE FROM users").
|
|
WillReturnResult(NewResult(1, 1))
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
go func() {
|
|
time.Sleep(time.Millisecond * 10)
|
|
cancel()
|
|
}()
|
|
|
|
res, err := db.ExecContext(ctx, "DELETE FROM users")
|
|
if err != nil {
|
|
t.Errorf("error was not expected, but got: %v", err)
|
|
}
|
|
|
|
affected, err := res.RowsAffected()
|
|
if affected != 1 {
|
|
t.Errorf("expected affected rows 1, but got %v", affected)
|
|
}
|
|
|
|
if err != nil {
|
|
t.Errorf("error was not expected, but got: %v", err)
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Errorf("there were unfulfilled expectations: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestContextQueryCancel(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
rs := NewRows([]string{"id", "title"}).AddRow(5, "hello world")
|
|
|
|
mock.ExpectQuery("SELECT (.+) FROM articles WHERE id = ?").
|
|
WithArgs(5).
|
|
WillDelayFor(time.Second).
|
|
WillReturnRows(rs)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
go func() {
|
|
time.Sleep(time.Millisecond * 10)
|
|
cancel()
|
|
}()
|
|
|
|
_, err = db.QueryContext(ctx, "SELECT id, title FROM articles WHERE id = ?", 5)
|
|
if err == nil {
|
|
t.Error("error was expected, but there was none")
|
|
}
|
|
|
|
if err != ErrCancelled {
|
|
t.Errorf("was expecting cancel error, but got: %v", err)
|
|
}
|
|
|
|
_, err = db.QueryContext(ctx, "SELECT id, title FROM articles WHERE id = ?", 5)
|
|
if err != context.Canceled {
|
|
t.Error("error was expected since context was already done, but there was none")
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Errorf("there were unfulfilled expectations: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestPreparedStatementContextQueryCancel(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
rs := NewRows([]string{"id", "title"}).AddRow(5, "hello world")
|
|
|
|
mock.ExpectPrepare("SELECT (.+) FROM articles WHERE id = ?").
|
|
ExpectQuery().
|
|
WithArgs(5).
|
|
WillDelayFor(time.Second).
|
|
WillReturnRows(rs)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
go func() {
|
|
time.Sleep(time.Millisecond * 10)
|
|
cancel()
|
|
}()
|
|
|
|
stmt, err := db.Prepare("SELECT id, title FROM articles WHERE id = ?")
|
|
if err != nil {
|
|
t.Errorf("error was not expected, but got: %v", err)
|
|
}
|
|
|
|
_, err = stmt.QueryContext(ctx, 5)
|
|
if err == nil {
|
|
t.Error("error was expected, but there was none")
|
|
}
|
|
|
|
if err != ErrCancelled {
|
|
t.Errorf("was expecting cancel error, but got: %v", err)
|
|
}
|
|
|
|
_, err = stmt.QueryContext(ctx, 5)
|
|
if err != context.Canceled {
|
|
t.Error("error was expected since context was already done, but there was none")
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Errorf("there were unfulfilled expectations: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestContextQuery(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
rs := NewRows([]string{"id", "title"}).AddRow(5, "hello world")
|
|
|
|
mock.ExpectQuery("SELECT (.+) FROM articles WHERE id =").
|
|
WithArgs(sql.Named("id", 5)).
|
|
WillDelayFor(time.Millisecond * 3).
|
|
WillReturnRows(rs)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
go func() {
|
|
time.Sleep(time.Millisecond * 10)
|
|
cancel()
|
|
}()
|
|
|
|
rows, err := db.QueryContext(ctx, "SELECT id, title FROM articles WHERE id = :id", sql.Named("id", 5))
|
|
if err != nil {
|
|
t.Errorf("error was not expected, but got: %v", err)
|
|
}
|
|
|
|
if !rows.Next() {
|
|
t.Error("expected one row, but there was none")
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Errorf("there were unfulfilled expectations: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestContextBeginCancel(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
mock.ExpectBegin().WillDelayFor(time.Second)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
go func() {
|
|
time.Sleep(time.Millisecond * 10)
|
|
cancel()
|
|
}()
|
|
|
|
_, err = db.BeginTx(ctx, nil)
|
|
if err == nil {
|
|
t.Error("error was expected, but there was none")
|
|
}
|
|
|
|
if err != ErrCancelled {
|
|
t.Errorf("was expecting cancel error, but got: %v", err)
|
|
}
|
|
|
|
_, err = db.BeginTx(ctx, nil)
|
|
if err != context.Canceled {
|
|
t.Error("error was expected since context was already done, but there was none")
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Errorf("there were unfulfilled expectations: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestContextBegin(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
mock.ExpectBegin().WillDelayFor(time.Millisecond * 3)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
go func() {
|
|
time.Sleep(time.Millisecond * 10)
|
|
cancel()
|
|
}()
|
|
|
|
tx, err := db.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
t.Errorf("error was not expected, but got: %v", err)
|
|
}
|
|
|
|
if tx == nil {
|
|
t.Error("expected tx, but there was nil")
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Errorf("there were unfulfilled expectations: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestContextPrepareCancel(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
mock.ExpectPrepare("SELECT").WillDelayFor(time.Second)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
go func() {
|
|
time.Sleep(time.Millisecond * 10)
|
|
cancel()
|
|
}()
|
|
|
|
_, err = db.PrepareContext(ctx, "SELECT")
|
|
if err == nil {
|
|
t.Error("error was expected, but there was none")
|
|
}
|
|
|
|
if err != ErrCancelled {
|
|
t.Errorf("was expecting cancel error, but got: %v", err)
|
|
}
|
|
|
|
_, err = db.PrepareContext(ctx, "SELECT")
|
|
if err != context.Canceled {
|
|
t.Error("error was expected since context was already done, but there was none")
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Errorf("there were unfulfilled expectations: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestContextPrepare(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
mock.ExpectPrepare("SELECT").WillDelayFor(time.Millisecond * 3)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
go func() {
|
|
time.Sleep(time.Millisecond * 10)
|
|
cancel()
|
|
}()
|
|
|
|
stmt, err := db.PrepareContext(ctx, "SELECT")
|
|
if err != nil {
|
|
t.Errorf("error was not expected, but got: %v", err)
|
|
}
|
|
|
|
if stmt == nil {
|
|
t.Error("expected stmt, but there was nil")
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Errorf("there were unfulfilled expectations: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestContextExecErrorDelay(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// test that return of error is delayed
|
|
var delay time.Duration
|
|
delay = 100 * time.Millisecond
|
|
mock.ExpectExec("^INSERT INTO articles").
|
|
WillReturnError(errors.New("slow fail")).
|
|
WillDelayFor(delay)
|
|
|
|
start := time.Now()
|
|
res, err := db.ExecContext(context.Background(), "INSERT INTO articles (title) VALUES (?)", "hello")
|
|
stop := time.Now()
|
|
|
|
if res != nil {
|
|
t.Errorf("result was not expected, was expecting nil")
|
|
}
|
|
|
|
if err == nil {
|
|
t.Errorf("error was expected, was not expecting nil")
|
|
}
|
|
|
|
if err.Error() != "slow fail" {
|
|
t.Errorf("error '%s' was not expected, was expecting '%s'", err.Error(), "slow fail")
|
|
}
|
|
|
|
elapsed := stop.Sub(start)
|
|
if elapsed < delay {
|
|
t.Errorf("expecting a delay of %v before error, actual delay was %v", delay, elapsed)
|
|
}
|
|
|
|
// also test that return of error is not delayed
|
|
mock.ExpectExec("^INSERT INTO articles").WillReturnError(errors.New("fast fail"))
|
|
|
|
start = time.Now()
|
|
db.ExecContext(context.Background(), "INSERT INTO articles (title) VALUES (?)", "hello")
|
|
stop = time.Now()
|
|
|
|
elapsed = stop.Sub(start)
|
|
if elapsed > delay {
|
|
t.Errorf("expecting a delay of less than %v before error, actual delay was %v", delay, elapsed)
|
|
}
|
|
}
|
|
|
|
// TestMonitorPingsDisabled verifies backwards-compatibility with behaviour of the library in which
|
|
// calls to Ping are not mocked out. It verifies this persists when the user does not enable the new
|
|
// behaviour.
|
|
func TestMonitorPingsDisabled(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// When monitoring of pings is not enabled in the mock, calling Ping should have no effect.
|
|
err = db.Ping()
|
|
if err != nil {
|
|
t.Errorf("monitoring of pings is not enabled so did not expect error from Ping, got '%s'", err)
|
|
}
|
|
|
|
// Calling ExpectPing should also not register any expectations in the mock. The return from
|
|
// ExpectPing should be nil.
|
|
expectation := mock.ExpectPing()
|
|
if expectation != nil {
|
|
t.Errorf("expected ExpectPing to return a nil pointer when monitoring of pings is not enabled")
|
|
}
|
|
|
|
err = mock.ExpectationsWereMet()
|
|
if err != nil {
|
|
t.Errorf("monitoring of pings is not enabled so ExpectPing should not register an expectation, got '%s'", err)
|
|
}
|
|
}
|
|
|
|
func TestPingExpectations(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New(MonitorPingsOption(true))
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
mock.ExpectPing()
|
|
if err := db.Ping(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Errorf("there were unfulfilled expectations: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestPingExpectationsErrorDelay(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New(MonitorPingsOption(true))
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
var delay time.Duration
|
|
delay = 100 * time.Millisecond
|
|
mock.ExpectPing().
|
|
WillReturnError(errors.New("slow fail")).
|
|
WillDelayFor(delay)
|
|
|
|
start := time.Now()
|
|
err = db.Ping()
|
|
stop := time.Now()
|
|
|
|
if err == nil {
|
|
t.Errorf("result was not expected, was not expecting nil error")
|
|
}
|
|
|
|
if err.Error() != "slow fail" {
|
|
t.Errorf("error '%s' was not expected, was expecting '%s'", err.Error(), "slow fail")
|
|
}
|
|
|
|
elapsed := stop.Sub(start)
|
|
if elapsed < delay {
|
|
t.Errorf("expecting a delay of %v before error, actual delay was %v", delay, elapsed)
|
|
}
|
|
|
|
mock.ExpectPing().WillReturnError(errors.New("fast fail"))
|
|
|
|
start = time.Now()
|
|
db.Ping()
|
|
stop = time.Now()
|
|
|
|
elapsed = stop.Sub(start)
|
|
if elapsed > delay {
|
|
t.Errorf("expecting a delay of less than %v before error, actual delay was %v", delay, elapsed)
|
|
}
|
|
}
|
|
|
|
func TestPingExpectationsMissingPing(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New(MonitorPingsOption(true))
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
mock.ExpectPing()
|
|
|
|
if err = mock.ExpectationsWereMet(); err == nil {
|
|
t.Fatalf("was expecting an error, but there wasn't one")
|
|
}
|
|
}
|
|
|
|
func TestPingExpectationsUnexpectedPing(t *testing.T) {
|
|
t.Parallel()
|
|
db, _, err := New(MonitorPingsOption(true))
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
if err = db.Ping(); err == nil {
|
|
t.Fatalf("was expecting an error, but there wasn't any")
|
|
}
|
|
}
|
|
|
|
func TestPingOrderedWrongOrder(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New(MonitorPingsOption(true))
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
mock.ExpectBegin()
|
|
mock.ExpectPing()
|
|
mock.MatchExpectationsInOrder(true)
|
|
|
|
if err = db.Ping(); err == nil {
|
|
t.Fatalf("was expecting an error, but there wasn't any")
|
|
}
|
|
}
|
|
|
|
func TestPingExpectationsContextTimeout(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New(MonitorPingsOption(true))
|
|
if err != nil {
|
|
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
mock.ExpectPing().WillDelayFor(time.Hour)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
|
|
defer cancel()
|
|
|
|
doneCh := make(chan struct{})
|
|
go func() {
|
|
err = db.PingContext(ctx)
|
|
close(doneCh)
|
|
}()
|
|
|
|
select {
|
|
case <-doneCh:
|
|
if err != ErrCancelled {
|
|
t.Errorf("expected error '%s' to be returned from Ping, but got '%s'", ErrCancelled, err)
|
|
}
|
|
case <-time.After(time.Second):
|
|
t.Errorf("expected Ping to return after context timeout, but it did not in a timely fashion")
|
|
}
|
|
}
|