github.com/blend/go-sdk@v1.20220411.3/testutil/db.go (about)

     1  /*
     2  
     3  Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file.
     5  
     6  */
     7  
     8  package testutil
     9  
    10  import (
    11  	"context"
    12  	"database/sql"
    13  	"reflect"
    14  	"unsafe"
    15  
    16  	"github.com/blend/go-sdk/db"
    17  	"github.com/blend/go-sdk/ex"
    18  )
    19  
    20  // NOTE: Ensure that
    21  //       * `AlwaysFailDB` satisfies `db.DB`.
    22  //       * `PseudoQueryDB` satisfies `db.DB`.
    23  var (
    24  	_ db.DB = (*AlwaysFailDB)(nil)
    25  	_ db.DB = (*PseudoQueryDB)(nil)
    26  )
    27  
    28  // AlwaysFailDB implements the `db.DB` interface, but each method always fails.
    29  type AlwaysFailDB struct {
    30  	Errors ErrorProducer
    31  }
    32  
    33  // ExecContext implements the `db.DB` interface and returns and error.
    34  func (afd *AlwaysFailDB) ExecContext(context.Context, string, ...interface{}) (sql.Result, error) {
    35  	return nil, afd.Errors.NextError()
    36  }
    37  
    38  // QueryContext implements the `db.DB` interface and returns and error.
    39  func (afd *AlwaysFailDB) QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) {
    40  	return nil, afd.Errors.NextError()
    41  }
    42  
    43  // QueryRowContext implements the `db.DB` interface; the error value is embedded
    44  // in the `sql.Row` value returned.
    45  func (afd *AlwaysFailDB) QueryRowContext(context.Context, string, ...interface{}) *sql.Row {
    46  	return sqlRowWithError(afd.Errors.NextError())
    47  }
    48  
    49  // PseudoQueryDB implements the `db.DB` interface, it intercepts calls to
    50  // `QueryContext` and replaces the `query` / `args` arguments with custom
    51  // values.
    52  type PseudoQueryDB struct {
    53  	DB    *sql.DB
    54  	Query string
    55  	Args  []interface{}
    56  }
    57  
    58  // ExecContext implements the `db.DB` interface; this is not supported in
    59  // `PseudoQueryDB`. It will **always** return a "not implemented" error.
    60  func (pqd *PseudoQueryDB) ExecContext(_ context.Context, _ string, _ ...interface{}) (sql.Result, error) {
    61  	return nil, ex.New("Not Implemented: ExecContext")
    62  }
    63  
    64  // QueryContext implements the `db.DB` interface. It intercepts the **actual**
    65  // query and arguments and replaces them with the query and arguments stored
    66  // on the current `PseudoQueryDB`.
    67  func (pqd *PseudoQueryDB) QueryContext(ctx context.Context, _ string, _ ...interface{}) (*sql.Rows, error) {
    68  	return pqd.DB.QueryContext(ctx, pqd.Query, pqd.Args...)
    69  }
    70  
    71  // QueryRowContext implements the `db.DB` interface; this is not supported in
    72  // `PseudoQueryDB`. It will **always** return a `sql.Row` with a "not implemented"
    73  // error set on the row.
    74  func (pqd *PseudoQueryDB) QueryRowContext(_ context.Context, _ string, _ ...interface{}) *sql.Row {
    75  	err := ex.New("Not Implemented: QueryRowContext")
    76  	return sqlRowWithError(err)
    77  }
    78  
    79  func sqlRowWithError(err error) *sql.Row {
    80  	row := &sql.Row{}
    81  	e := reflect.ValueOf(row).Elem().FieldByName("err")
    82  	eMutable := reflect.NewAt(e.Type(), unsafe.Pointer(e.UnsafeAddr())).Elem()
    83  	eMutable.Set(reflect.ValueOf(err))
    84  	return row
    85  }