mirror of
https://github.com/zhashkevych/go-sqlxmock.git
synced 2024-11-16 17:41:57 +02:00
d5879ee4b7
The intention of sql.RawBytes is for it to hold memory owned by the database. When used, it's content is only valid until the `Next`, `Scan` or `Close` is called on the `Rows` To ensure that we meet this behaviour, when `[]byte` is used in a column, it's value is copied to a buffer that we keep track of for later invalidation. By doing this, incorrect use of `sql.RawBytes` values is exposed in tests that use go-sqlmock. Without this, when a real database is used and it's driver does share memory, then those issues would not be exposed until runtime (and in non-obvious ways)
673 lines
18 KiB
Go
673 lines
18 KiB
Go
package sqlmock
|
|
|
|
import (
|
|
"bytes"
|
|
"database/sql"
|
|
"fmt"
|
|
"testing"
|
|
)
|
|
|
|
const invalid = `☠☠☠ MEMORY OVERWRITTEN ☠☠☠ `
|
|
|
|
func ExampleRows() {
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
fmt.Println("failed to open sqlmock database:", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
rows := NewRows([]string{"id", "title"}).
|
|
AddRow(1, "one").
|
|
AddRow(2, "two")
|
|
|
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
|
|
|
rs, _ := db.Query("SELECT")
|
|
defer rs.Close()
|
|
|
|
for rs.Next() {
|
|
var id int
|
|
var title string
|
|
rs.Scan(&id, &title)
|
|
fmt.Println("scanned id:", id, "and title:", title)
|
|
}
|
|
|
|
if rs.Err() != nil {
|
|
fmt.Println("got rows error:", rs.Err())
|
|
}
|
|
// Output: scanned id: 1 and title: one
|
|
// scanned id: 2 and title: two
|
|
}
|
|
|
|
func ExampleRows_rowError() {
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
fmt.Println("failed to open sqlmock database:", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
rows := NewRows([]string{"id", "title"}).
|
|
AddRow(0, "one").
|
|
AddRow(1, "two").
|
|
RowError(1, fmt.Errorf("row error"))
|
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
|
|
|
rs, _ := db.Query("SELECT")
|
|
defer rs.Close()
|
|
|
|
for rs.Next() {
|
|
var id int
|
|
var title string
|
|
rs.Scan(&id, &title)
|
|
fmt.Println("scanned id:", id, "and title:", title)
|
|
}
|
|
|
|
if rs.Err() != nil {
|
|
fmt.Println("got rows error:", rs.Err())
|
|
}
|
|
// Output: scanned id: 0 and title: one
|
|
// got rows error: row error
|
|
}
|
|
|
|
func ExampleRows_closeError() {
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
fmt.Println("failed to open sqlmock database:", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
rows := NewRows([]string{"id", "title"}).CloseError(fmt.Errorf("close error"))
|
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
|
|
|
rs, _ := db.Query("SELECT")
|
|
|
|
// Note: that close will return error only before rows EOF
|
|
// that is a default sql package behavior. If you run rs.Next()
|
|
// it will handle the error internally and return nil bellow
|
|
if err := rs.Close(); err != nil {
|
|
fmt.Println("got error:", err)
|
|
}
|
|
|
|
// Output: got error: close error
|
|
}
|
|
|
|
func ExampleRows_rawBytes() {
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
fmt.Println("failed to open sqlmock database:", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
rows := NewRows([]string{"id", "binary"}).
|
|
AddRow(1, []byte(`one binary value with some text!`)).
|
|
AddRow(2, []byte(`two binary value with even more text than the first one`))
|
|
|
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
|
|
|
rs, _ := db.Query("SELECT")
|
|
defer rs.Close()
|
|
|
|
type scanned struct {
|
|
id int
|
|
raw sql.RawBytes
|
|
}
|
|
fmt.Println("initial read...")
|
|
var ss []scanned
|
|
for rs.Next() {
|
|
var s scanned
|
|
rs.Scan(&s.id, &s.raw)
|
|
ss = append(ss, s)
|
|
fmt.Println("scanned id:", s.id, "and raw:", string(s.raw))
|
|
}
|
|
|
|
if rs.Err() != nil {
|
|
fmt.Println("got rows error:", rs.Err())
|
|
}
|
|
|
|
fmt.Println("after reading all...")
|
|
for _, s := range ss {
|
|
fmt.Println("scanned id:", s.id, "and raw:", string(s.raw))
|
|
}
|
|
// Output:
|
|
// initial read...
|
|
// scanned id: 1 and raw: one binary value with some text!
|
|
// scanned id: 2 and raw: two binary value with even more text than the first one
|
|
// after reading all...
|
|
// scanned id: 1 and raw: ☠☠☠ MEMORY OVERWRITTEN ☠
|
|
// scanned id: 2 and raw: ☠☠☠ MEMORY OVERWRITTEN ☠☠☠ ☠☠☠ MEMORY
|
|
}
|
|
|
|
func ExampleRows_expectToBeClosed() {
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
fmt.Println("failed to open sqlmock database:", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
rows := NewRows([]string{"id", "title"}).AddRow(1, "john")
|
|
mock.ExpectQuery("SELECT").WillReturnRows(rows).RowsWillBeClosed()
|
|
|
|
db.Query("SELECT")
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
fmt.Println("got error:", err)
|
|
}
|
|
|
|
// Output: got error: expected query rows to be closed, but it was not: ExpectedQuery => expecting Query, QueryContext or QueryRow which:
|
|
// - matches sql: 'SELECT'
|
|
// - is without arguments
|
|
// - should return rows:
|
|
// row 0 - [1 john]
|
|
}
|
|
|
|
func ExampleRows_customDriverValue() {
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
fmt.Println("failed to open sqlmock database:", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
rows := NewRows([]string{"id", "null_int"}).
|
|
AddRow(1, 7).
|
|
AddRow(5, sql.NullInt64{Int64: 5, Valid: true}).
|
|
AddRow(2, sql.NullInt64{})
|
|
|
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
|
|
|
rs, _ := db.Query("SELECT")
|
|
defer rs.Close()
|
|
|
|
for rs.Next() {
|
|
var id int
|
|
var num sql.NullInt64
|
|
rs.Scan(&id, &num)
|
|
fmt.Println("scanned id:", id, "and null int64:", num)
|
|
}
|
|
|
|
if rs.Err() != nil {
|
|
fmt.Println("got rows error:", rs.Err())
|
|
}
|
|
// Output: scanned id: 1 and null int64: {7 true}
|
|
// scanned id: 5 and null int64: {5 true}
|
|
// scanned id: 2 and null int64: {0 false}
|
|
}
|
|
|
|
func TestAllowsToSetRowsErrors(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
rows := NewRows([]string{"id", "title"}).
|
|
AddRow(0, "one").
|
|
AddRow(1, "two").
|
|
RowError(1, fmt.Errorf("error"))
|
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
|
|
|
rs, err := db.Query("SELECT")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
defer rs.Close()
|
|
|
|
if !rs.Next() {
|
|
t.Fatal("expected the first row to be available")
|
|
}
|
|
if rs.Err() != nil {
|
|
t.Fatalf("unexpected error: %s", rs.Err())
|
|
}
|
|
|
|
if rs.Next() {
|
|
t.Fatal("was not expecting the second row, since there should be an error")
|
|
}
|
|
if rs.Err() == nil {
|
|
t.Fatal("expected an error, but got none")
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestRowsCloseError(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
rows := NewRows([]string{"id"}).CloseError(fmt.Errorf("close error"))
|
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
|
|
|
rs, err := db.Query("SELECT")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
|
|
if err := rs.Close(); err == nil {
|
|
t.Fatal("expected a close error")
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestRowsClosed(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
rows := NewRows([]string{"id"}).AddRow(1)
|
|
mock.ExpectQuery("SELECT").WillReturnRows(rows).RowsWillBeClosed()
|
|
|
|
rs, err := db.Query("SELECT")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
|
|
if err := rs.Close(); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestQuerySingleRow(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
rows := NewRows([]string{"id"}).
|
|
AddRow(1).
|
|
AddRow(2)
|
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
|
|
|
var id int
|
|
if err := db.QueryRow("SELECT").Scan(&id); err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
|
|
mock.ExpectQuery("SELECT").WillReturnRows(NewRows([]string{"id"}))
|
|
if err := db.QueryRow("SELECT").Scan(&id); err != sql.ErrNoRows {
|
|
t.Fatal("expected sql no rows error")
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestQueryRowBytesInvalidatedByNext_bytesIntoRawBytes(t *testing.T) {
|
|
t.Parallel()
|
|
replace := []byte(invalid)
|
|
rows := NewRows([]string{"raw"}).
|
|
AddRow([]byte(`one binary value with some text!`)).
|
|
AddRow([]byte(`two binary value with even more text than the first one`))
|
|
scan := func(rs *sql.Rows) ([]byte, error) {
|
|
var raw sql.RawBytes
|
|
return raw, rs.Scan(&raw)
|
|
}
|
|
want := []struct {
|
|
Initial []byte
|
|
Replaced []byte
|
|
}{
|
|
{Initial: []byte(`one binary value with some text!`), Replaced: replace[:len(replace)-7]},
|
|
{Initial: []byte(`two binary value with even more text than the first one`), Replaced: bytes.Join([][]byte{replace, replace[:len(replace)-23]}, nil)},
|
|
}
|
|
queryRowBytesInvalidatedByNext(t, rows, scan, want)
|
|
}
|
|
|
|
func TestQueryRowBytesNotInvalidatedByNext_bytesIntoBytes(t *testing.T) {
|
|
t.Parallel()
|
|
rows := NewRows([]string{"raw"}).
|
|
AddRow([]byte(`one binary value with some text!`)).
|
|
AddRow([]byte(`two binary value with even more text than the first one`))
|
|
scan := func(rs *sql.Rows) ([]byte, error) {
|
|
var b []byte
|
|
return b, rs.Scan(&b)
|
|
}
|
|
want := [][]byte{[]byte(`one binary value with some text!`), []byte(`two binary value with even more text than the first one`)}
|
|
queryRowBytesNotInvalidatedByNext(t, rows, scan, want)
|
|
}
|
|
|
|
func TestQueryRowBytesNotInvalidatedByNext_stringIntoBytes(t *testing.T) {
|
|
t.Parallel()
|
|
rows := NewRows([]string{"raw"}).
|
|
AddRow(`one binary value with some text!`).
|
|
AddRow(`two binary value with even more text than the first one`)
|
|
scan := func(rs *sql.Rows) ([]byte, error) {
|
|
var b []byte
|
|
return b, rs.Scan(&b)
|
|
}
|
|
want := [][]byte{[]byte(`one binary value with some text!`), []byte(`two binary value with even more text than the first one`)}
|
|
queryRowBytesNotInvalidatedByNext(t, rows, scan, want)
|
|
}
|
|
|
|
func TestQueryRowBytesInvalidatedByClose_bytesIntoRawBytes(t *testing.T) {
|
|
t.Parallel()
|
|
replace := []byte(invalid)
|
|
rows := NewRows([]string{"raw"}).AddRow([]byte(`one binary value with some text!`))
|
|
scan := func(rs *sql.Rows) ([]byte, error) {
|
|
var raw sql.RawBytes
|
|
return raw, rs.Scan(&raw)
|
|
}
|
|
want := struct {
|
|
Initial []byte
|
|
Replaced []byte
|
|
}{
|
|
Initial: []byte(`one binary value with some text!`),
|
|
Replaced: replace[:len(replace)-7],
|
|
}
|
|
queryRowBytesInvalidatedByClose(t, rows, scan, want)
|
|
}
|
|
|
|
func TestQueryRowBytesNotInvalidatedByClose_bytesIntoBytes(t *testing.T) {
|
|
t.Parallel()
|
|
rows := NewRows([]string{"raw"}).AddRow([]byte(`one binary value with some text!`))
|
|
scan := func(rs *sql.Rows) ([]byte, error) {
|
|
var b []byte
|
|
return b, rs.Scan(&b)
|
|
}
|
|
queryRowBytesNotInvalidatedByClose(t, rows, scan, []byte(`one binary value with some text!`))
|
|
}
|
|
|
|
func TestQueryRowBytesNotInvalidatedByClose_stringIntoBytes(t *testing.T) {
|
|
t.Parallel()
|
|
rows := NewRows([]string{"raw"}).AddRow(`one binary value with some text!`)
|
|
scan := func(rs *sql.Rows) ([]byte, error) {
|
|
var b []byte
|
|
return b, rs.Scan(&b)
|
|
}
|
|
queryRowBytesNotInvalidatedByClose(t, rows, scan, []byte(`one binary value with some text!`))
|
|
}
|
|
|
|
func TestRowsScanError(t *testing.T) {
|
|
t.Parallel()
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
r := NewRows([]string{"col1", "col2"}).AddRow("one", "two").AddRow("one", nil)
|
|
mock.ExpectQuery("SELECT").WillReturnRows(r)
|
|
|
|
rs, err := db.Query("SELECT")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
defer rs.Close()
|
|
|
|
var one, two string
|
|
if !rs.Next() || rs.Err() != nil || rs.Scan(&one, &two) != nil {
|
|
t.Fatal("unexpected error on first row scan")
|
|
}
|
|
|
|
if !rs.Next() || rs.Err() != nil {
|
|
t.Fatal("unexpected error on second row read")
|
|
}
|
|
|
|
err = rs.Scan(&one, &two)
|
|
if err == nil {
|
|
t.Fatal("expected an error for scan, but got none")
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestCSVRowParser(t *testing.T) {
|
|
t.Parallel()
|
|
rs := NewRows([]string{"col1", "col2"}).FromCSVString("a,NULL")
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
mock.ExpectQuery("SELECT").WillReturnRows(rs)
|
|
|
|
rw, err := db.Query("SELECT")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
defer rw.Close()
|
|
var col1 string
|
|
var col2 []byte
|
|
|
|
rw.Next()
|
|
if err = rw.Scan(&col1, &col2); err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
if col1 != "a" {
|
|
t.Fatalf("expected col1 to be 'a', but got [%T]:%+v", col1, col1)
|
|
}
|
|
if col2 != nil {
|
|
t.Fatalf("expected col2 to be nil, but got [%T]:%+v", col2, col2)
|
|
}
|
|
}
|
|
|
|
func TestWrongNumberOfValues(t *testing.T) {
|
|
// Open new mock database
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
fmt.Println("error creating mock database")
|
|
return
|
|
}
|
|
defer db.Close()
|
|
defer func() {
|
|
recover()
|
|
}()
|
|
mock.ExpectQuery("SELECT ID FROM TABLE").WithArgs(101).WillReturnRows(NewRows([]string{"ID"}).AddRow(101, "Hello"))
|
|
db.Query("SELECT ID FROM TABLE", 101)
|
|
// shouldn't reach here
|
|
t.Error("expected panic from query")
|
|
}
|
|
|
|
func TestEmptyRowSets(t *testing.T) {
|
|
rs1 := NewRows([]string{"a"}).AddRow("a")
|
|
rs2 := NewRows([]string{"b"})
|
|
rs3 := NewRows([]string{"c"})
|
|
|
|
set1 := &rowSets{sets: []*Rows{rs1, rs2}}
|
|
set2 := &rowSets{sets: []*Rows{rs3, rs2}}
|
|
set3 := &rowSets{sets: []*Rows{rs2}}
|
|
|
|
if set1.empty() {
|
|
t.Fatalf("expected rowset 1, not to be empty, but it was")
|
|
}
|
|
if !set2.empty() {
|
|
t.Fatalf("expected rowset 2, to be empty, but it was not")
|
|
}
|
|
if !set3.empty() {
|
|
t.Fatalf("expected rowset 3, to be empty, but it was not")
|
|
}
|
|
}
|
|
|
|
func queryRowBytesInvalidatedByNext(t *testing.T, rows *Rows, scan func(*sql.Rows) ([]byte, error), want []struct {
|
|
Initial []byte
|
|
Replaced []byte
|
|
}) {
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
|
|
|
rs, err := db.Query("SELECT")
|
|
if err != nil {
|
|
t.Fatalf("failed to query rows: %s", err)
|
|
}
|
|
|
|
if !rs.Next() || rs.Err() != nil {
|
|
t.Fatal("unexpected error on first row retrieval")
|
|
}
|
|
var count int
|
|
for i := 0; ; i++ {
|
|
count++
|
|
b, err := scan(rs)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error scanning row: %s", err)
|
|
}
|
|
if exp := want[i].Initial; !bytes.Equal(b, exp) {
|
|
t.Fatalf("expected raw value to be '%s' (len:%d), but got [%T]:%s (len:%d)", exp, len(exp), b, b, len(b))
|
|
}
|
|
next := rs.Next()
|
|
if exp := want[i].Replaced; !bytes.Equal(b, exp) {
|
|
t.Fatalf("expected raw value to be replaced with '%s' (len:%d) after calling Next(), but got [%T]:%s (len:%d)", exp, len(exp), b, b, len(b))
|
|
}
|
|
if !next {
|
|
break
|
|
}
|
|
}
|
|
if err := rs.Err(); err != nil {
|
|
t.Fatalf("row iteration failed: %s", err)
|
|
}
|
|
if exp := len(want); count != exp {
|
|
t.Fatalf("incorrect number of rows exp: %d, but got %d", exp, count)
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func queryRowBytesNotInvalidatedByNext(t *testing.T, rows *Rows, scan func(*sql.Rows) ([]byte, error), want [][]byte) {
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
|
|
|
rs, err := db.Query("SELECT")
|
|
if err != nil {
|
|
t.Fatalf("failed to query rows: %s", err)
|
|
}
|
|
|
|
if !rs.Next() || rs.Err() != nil {
|
|
t.Fatal("unexpected error on first row retrieval")
|
|
}
|
|
var count int
|
|
for i := 0; ; i++ {
|
|
count++
|
|
b, err := scan(rs)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error scanning row: %s", err)
|
|
}
|
|
if exp := want[i]; !bytes.Equal(b, exp) {
|
|
t.Fatalf("expected raw value to be '%s' (len:%d), but got [%T]:%s (len:%d)", exp, len(exp), b, b, len(b))
|
|
}
|
|
next := rs.Next()
|
|
if exp := want[i]; !bytes.Equal(b, exp) {
|
|
t.Fatalf("expected raw value to be replaced with '%s' (len:%d) after calling Next(), but got [%T]:%s (len:%d)", exp, len(exp), b, b, len(b))
|
|
}
|
|
if !next {
|
|
break
|
|
}
|
|
}
|
|
if err := rs.Err(); err != nil {
|
|
t.Fatalf("row iteration failed: %s", err)
|
|
}
|
|
if exp := len(want); count != exp {
|
|
t.Fatalf("incorrect number of rows exp: %d, but got %d", exp, count)
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func queryRowBytesInvalidatedByClose(t *testing.T, rows *Rows, scan func(*sql.Rows) ([]byte, error), want struct {
|
|
Initial []byte
|
|
Replaced []byte
|
|
}) {
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
|
|
|
rs, err := db.Query("SELECT")
|
|
if err != nil {
|
|
t.Fatalf("failed to query rows: %s", err)
|
|
}
|
|
|
|
if !rs.Next() || rs.Err() != nil {
|
|
t.Fatal("unexpected error on first row retrieval")
|
|
}
|
|
b, err := scan(rs)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error scanning row: %s", err)
|
|
}
|
|
if !bytes.Equal(b, want.Initial) {
|
|
t.Fatalf("expected raw value to be '%s' (len:%d), but got [%T]:%s (len:%d)", want.Initial, len(want.Initial), b, b, len(b))
|
|
}
|
|
if err := rs.Close(); err != nil {
|
|
t.Fatalf("unexpected error closing rows: %s", err)
|
|
}
|
|
if !bytes.Equal(b, want.Replaced) {
|
|
t.Fatalf("expected raw value to be replaced with '%s' (len:%d) after calling Next(), but got [%T]:%s (len:%d)", want.Replaced, len(want.Replaced), b, b, len(b))
|
|
}
|
|
if err := rs.Err(); err != nil {
|
|
t.Fatalf("row iteration failed: %s", err)
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func queryRowBytesNotInvalidatedByClose(t *testing.T, rows *Rows, scan func(*sql.Rows) ([]byte, error), want []byte) {
|
|
db, mock, err := New()
|
|
if err != nil {
|
|
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
|
}
|
|
defer db.Close()
|
|
mock.ExpectQuery("SELECT").WillReturnRows(rows)
|
|
|
|
rs, err := db.Query("SELECT")
|
|
if err != nil {
|
|
t.Fatalf("failed to query rows: %s", err)
|
|
}
|
|
|
|
if !rs.Next() || rs.Err() != nil {
|
|
t.Fatal("unexpected error on first row retrieval")
|
|
}
|
|
b, err := scan(rs)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error scanning row: %s", err)
|
|
}
|
|
if !bytes.Equal(b, want) {
|
|
t.Fatalf("expected raw value to be '%s' (len:%d), but got [%T]:%s (len:%d)", want, len(want), b, b, len(b))
|
|
}
|
|
if err := rs.Close(); err != nil {
|
|
t.Fatalf("unexpected error closing rows: %s", err)
|
|
}
|
|
if !bytes.Equal(b, want) {
|
|
t.Fatalf("expected raw value to be replaced with '%s' (len:%d) after calling Next(), but got [%T]:%s (len:%d)", want, len(want), b, b, len(b))
|
|
}
|
|
if err := rs.Err(); err != nil {
|
|
t.Fatalf("row iteration failed: %s", err)
|
|
}
|
|
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|