pashagolub / pgxmock

pgx mock driver for golang to test database interactions
Other
388 stars 49 forks source link

Scanning a single row without using rows.Next() #174

Closed krispetkov closed 11 months ago

krispetkov commented 11 months ago

Describe the bug If we have the following function for example:

func (r *Repository) GetIdByName(ctx context.Context, name string) (int, error) {
    rows, err := sr.db.Query(ctx, "SELECT id FROM table WHERE name = $1", name)
    if err != nil {
        return -1, err
    }
    defer rows.Close()

    var id int
    if err := rows.Scan(&id); err != nil {
        return -1, err
    }

    return id, nil
}

When trying to mockit and test it with the following test:

Describe("GetIdName", func() {
        It("should return id", func() {
            mockStoreId := 1

            rows := pgxmock.NewRows([]string{"id"}).
                AddRow(mockStoreId)

            mockDB.ExpectQuery(`SELECT id FROM table WHERE name = \$1`).
                WithArgs("fake-name").
                WillReturnRows(rows)

            storeId, err := repository.GetIdByName(ctx, "fake-name")
            Expect(err).NotTo(HaveOccurred())
            Expect(storeId).To(Equal(mockStoreId))
        })
    })

We get runtime error: index out of range [-1]

Proposed fix in the linked PR below

To Reproduce Steps to reproduce the behavior:

  1. Create a function with a scan without rows.Next()
  2. Mock it
  3. See error

Expected behavior To be able to mock scans without rows.Next()

Screenshots N/A

Fix https://github.com/pashagolub/pgxmock/pull/175

pashagolub commented 11 months ago

Not a bug. According to the pgx manual:

Scan reads the values from the current row into dest values positionally. dest can include pointers to core types, values implementing the Scanner interface, and nil. nil will skip the value entirely. It is an error to call Scan without first calling Next() and checking that it returned true.

pashagolub commented 11 months ago

It is easy to check with this code:

func main() {
    db, err := pgxpool.New(context.Background(), "postgresql://user:pwd@localhost:5432/dbname")
    if err != nil {
        panic(err)
    }
    defer db.Close()

    r, err := db.Query(context.Background(), "SELECT 42")
    if err != nil {
        panic(err)
    }
    var id int
    err = r.Scan(&id)
    if err != nil {
        panic(fmt.Errorf("One cannot call Scan() without Next(): %w", err))
    }
}

Output:

panic: One cannot call Scan() without Next(): number of field descriptions must equal number of values, got 1 and 0

goroutine 1 [running]:
main.main()
        C:/Users/pasha/Code/pgxmock/examples/basic/basic.go:55 +0x1c5
exit status 2