github.com/DeltaLaboratory/entcache@v0.1.1/driver_test.go (about)

     1  package entcache_test
     2  
     3  import (
     4  	"context"
     5  	"database/sql/driver"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/redis/rueidis"
    10  	"go.uber.org/mock/gomock"
    11  
    12  	"github.com/DeltaLaboratory/entcache"
    13  
    14  	"entgo.io/ent/dialect"
    15  	"entgo.io/ent/dialect/sql"
    16  	"github.com/DATA-DOG/go-sqlmock"
    17  	ruemock "github.com/redis/rueidis/mock"
    18  )
    19  
    20  func TestDriver_ContextLevel(t *testing.T) {
    21  	db, mock, err := sqlmock.New()
    22  	if err != nil {
    23  		t.Fatal(err)
    24  	}
    25  	drv := sql.OpenDB(dialect.MySQL, db)
    26  
    27  	t.Run("One", func(t *testing.T) {
    28  		drv := entcache.NewDriver(drv, entcache.ContextLevel())
    29  		mock.ExpectQuery("SELECT id FROM users").
    30  			WillReturnRows(
    31  				sqlmock.NewRows([]string{"id"}).
    32  					AddRow(1).
    33  					AddRow(2).
    34  					AddRow(3),
    35  			)
    36  		ctx := entcache.NewContext(context.Background())
    37  		expectQuery(ctx, t, drv, "SELECT id FROM users", []interface{}{int64(1), int64(2), int64(3)})
    38  		expectQuery(ctx, t, drv, "SELECT id FROM users", []interface{}{int64(1), int64(2), int64(3)})
    39  		if err := mock.ExpectationsWereMet(); err != nil {
    40  			t.Fatal(err)
    41  		}
    42  	})
    43  
    44  	t.Run("Multi", func(t *testing.T) {
    45  		drv := entcache.NewDriver(drv, entcache.ContextLevel())
    46  		mock.ExpectQuery("SELECT name FROM users").
    47  			WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("a8m"))
    48  		ctx1 := entcache.NewContext(context.Background())
    49  		expectQuery(ctx1, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
    50  		ctx2 := entcache.NewContext(context.Background())
    51  		mock.ExpectQuery("SELECT name FROM users").
    52  			WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("a8m"))
    53  		expectQuery(ctx2, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
    54  		if err := mock.ExpectationsWereMet(); err != nil {
    55  			t.Fatal(err)
    56  		}
    57  	})
    58  
    59  	t.Run("TTL", func(t *testing.T) {
    60  		drv := entcache.NewDriver(drv, entcache.ContextLevel(), entcache.TTL(-1))
    61  		mock.ExpectQuery("SELECT name FROM users").
    62  			WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("a8m"))
    63  		mock.ExpectQuery("SELECT name FROM users").
    64  			WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("a8m"))
    65  		ctx := entcache.NewContext(context.Background())
    66  		expectQuery(ctx, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
    67  		expectQuery(ctx, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
    68  		if err := mock.ExpectationsWereMet(); err != nil {
    69  			t.Fatal(err)
    70  		}
    71  	})
    72  }
    73  
    74  func TestDriver_Levels(t *testing.T) {
    75  	db, mock, err := sqlmock.New()
    76  	if err != nil {
    77  		t.Fatal(err)
    78  	}
    79  	drv := sql.OpenDB(dialect.Postgres, db)
    80  
    81  	t.Run("One", func(t *testing.T) {
    82  		drv := entcache.NewDriver(drv, entcache.TTL(time.Second))
    83  		mock.ExpectQuery("SELECT age FROM users").
    84  			WillReturnRows(
    85  				sqlmock.NewRows([]string{"age"}).
    86  					AddRow(20.1).
    87  					AddRow(30.2).
    88  					AddRow(40.5),
    89  			)
    90  		expectQuery(context.Background(), t, drv, "SELECT age FROM users", []interface{}{20.1, 30.2, 40.5})
    91  		expectQuery(context.Background(), t, drv, "SELECT age FROM users", []interface{}{20.1, 30.2, 40.5})
    92  		if err := mock.ExpectationsWereMet(); err != nil {
    93  			t.Fatal(err)
    94  		}
    95  	})
    96  
    97  	t.Run("Multi", func(t *testing.T) {
    98  		drv := entcache.NewDriver(
    99  			drv,
   100  			entcache.Levels(
   101  				entcache.NewLRU(-1), // Nop.
   102  				entcache.NewLRU(0),  // No limit.
   103  			),
   104  		)
   105  		mock.ExpectQuery("SELECT age FROM users").
   106  			WillReturnRows(
   107  				sqlmock.NewRows([]string{"age"}).
   108  					AddRow(20.1).
   109  					AddRow(30.2).
   110  					AddRow(40.5),
   111  			)
   112  		expectQuery(context.Background(), t, drv, "SELECT age FROM users", []interface{}{20.1, 30.2, 40.5})
   113  		expectQuery(context.Background(), t, drv, "SELECT age FROM users", []interface{}{20.1, 30.2, 40.5})
   114  		if err := mock.ExpectationsWereMet(); err != nil {
   115  			t.Fatal(err)
   116  		}
   117  	})
   118  
   119  	t.Run("Redis", func(t *testing.T) {
   120  		var (
   121  			rdb = ruemock.NewClient(gomock.NewController(t))
   122  			drv = entcache.NewDriver(
   123  				drv,
   124  				entcache.Levels(
   125  					entcache.NewLRU(-1),
   126  					entcache.NewRedis(rdb),
   127  				),
   128  				entcache.Hash(func(string, []interface{}) (entcache.Key, error) {
   129  					return 1, nil
   130  				}),
   131  			)
   132  		)
   133  		rdb.EXPECT().Do(context.Background(), ruemock.Match("GET", "1")).Return(ruemock.Result(ruemock.RedisNil()))
   134  		mock.ExpectQuery("SELECT active FROM users").
   135  			WillReturnRows(sqlmock.NewRows([]string{"active"}).AddRow(true).AddRow(false))
   136  
   137  		buf, _ := entcache.Entry{Values: [][]driver.Value{{true}, {false}}}.MarshalBinary()
   138  		rdb.EXPECT().Do(context.Background(), ruemock.Match("SET", "1", rueidis.BinaryString(buf), "EX", "0")).Return(ruemock.Result(ruemock.RedisNil()))
   139  		expectQuery(context.Background(), t, drv, "SELECT active FROM users", []interface{}{true, false})
   140  
   141  		rdb.EXPECT().Do(context.Background(), ruemock.Match("GET", "1")).Return(ruemock.Result(ruemock.RedisString(rueidis.BinaryString(buf))))
   142  		expectQuery(context.Background(), t, drv, "SELECT active FROM users", []interface{}{true, false})
   143  
   144  		expected := entcache.Stats{Gets: 2, Hits: 1}
   145  		if s := drv.Stats(); s != expected {
   146  			t.Errorf("unexpected stats: %v != %v", s, expected)
   147  		}
   148  	})
   149  }
   150  
   151  func TestDriver_ContextOptions(t *testing.T) {
   152  	db, mock, err := sqlmock.New()
   153  	if err != nil {
   154  		t.Fatal(err)
   155  	}
   156  	drv := sql.OpenDB(dialect.MySQL, db)
   157  
   158  	t.Run("Skip", func(t *testing.T) {
   159  		drv := entcache.NewDriver(drv)
   160  		mock.ExpectQuery("SELECT name FROM users").
   161  			WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("a8m"))
   162  		ctx := context.Background()
   163  		expectQuery(ctx, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
   164  		expectQuery(ctx, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
   165  		mock.ExpectQuery("SELECT name FROM users").
   166  			WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("a8m"))
   167  		skipCtx := entcache.Skip(ctx)
   168  		expectQuery(skipCtx, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
   169  		expectQuery(ctx, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
   170  		if err := mock.ExpectationsWereMet(); err != nil {
   171  			t.Fatal(err)
   172  		}
   173  	})
   174  
   175  	t.Run("Evict", func(t *testing.T) {
   176  		drv := entcache.NewDriver(drv)
   177  		mock.ExpectQuery("SELECT name FROM users").
   178  			WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("a8m"))
   179  		ctx := context.Background()
   180  		expectQuery(ctx, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
   181  		expectQuery(ctx, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
   182  		mock.ExpectQuery("SELECT name FROM users").
   183  			WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("a8m"))
   184  		evictCtx := entcache.Evict(ctx)
   185  		expectQuery(evictCtx, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
   186  		mock.ExpectQuery("SELECT name FROM users").
   187  			WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("a8m"))
   188  		expectQuery(ctx, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
   189  		if err := mock.ExpectationsWereMet(); err != nil {
   190  			t.Fatal(err)
   191  		}
   192  	})
   193  
   194  	t.Run("WithTTL", func(t *testing.T) {
   195  		drv := entcache.NewDriver(drv)
   196  		mock.ExpectQuery("SELECT name FROM users").
   197  			WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("a8m"))
   198  		ttlCtx := entcache.WithTTL(context.Background(), -1)
   199  		expectQuery(ttlCtx, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
   200  		mock.ExpectQuery("SELECT name FROM users").
   201  			WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("a8m"))
   202  		expectQuery(ttlCtx, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
   203  		if err := mock.ExpectationsWereMet(); err != nil {
   204  			t.Fatal(err)
   205  		}
   206  	})
   207  
   208  	t.Run("WithKey", func(t *testing.T) {
   209  		drv := entcache.NewDriver(drv)
   210  		mock.ExpectQuery("SELECT name FROM users").
   211  			WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("a8m"))
   212  		ctx := context.Background()
   213  		keyCtx := entcache.WithKey(ctx, "cache-key")
   214  		expectQuery(keyCtx, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
   215  		expectQuery(keyCtx, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
   216  		mock.ExpectQuery("SELECT name FROM users").
   217  			WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("a8m"))
   218  		expectQuery(ctx, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
   219  		if err := drv.Cache.Del(ctx, "cache-key"); err != nil {
   220  			t.Fatal(err)
   221  		}
   222  		mock.ExpectQuery("SELECT name FROM users").
   223  			WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow("a8m"))
   224  		expectQuery(keyCtx, t, drv, "SELECT name FROM users", []interface{}{"a8m"})
   225  		if err := mock.ExpectationsWereMet(); err != nil {
   226  			t.Fatal(err)
   227  		}
   228  		expected := entcache.Stats{Gets: 4, Hits: 1}
   229  		if s := drv.Stats(); s != expected {
   230  			t.Errorf("unexpected stats: %v != %v", s, expected)
   231  		}
   232  	})
   233  }
   234  
   235  func TestDriver_SkipInsert(t *testing.T) {
   236  	db, mock, err := sqlmock.New()
   237  	if err != nil {
   238  		t.Fatal(err)
   239  	}
   240  	drv := entcache.NewDriver(sql.OpenDB(dialect.Postgres, db), entcache.Hash(func(string, []interface{}) (entcache.Key, error) {
   241  		t.Fatal("Driver.Query should not be called for INSERT statements")
   242  		return nil, nil
   243  	}))
   244  	mock.ExpectQuery("INSERT INTO users DEFAULT VALUES RETURNING id").
   245  		WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1))
   246  	expectQuery(context.Background(), t, drv, "INSERT INTO users DEFAULT VALUES RETURNING id", []interface{}{int64(1)})
   247  	if err := mock.ExpectationsWereMet(); err != nil {
   248  		t.Fatal(err)
   249  	}
   250  	var expected entcache.Stats
   251  	if s := drv.Stats(); s != expected {
   252  		t.Errorf("unexpected stats: %v != %v", s, expected)
   253  	}
   254  }
   255  
   256  func expectQuery(ctx context.Context, t *testing.T, drv dialect.Driver, query string, args []interface{}) {
   257  	rows := &sql.Rows{}
   258  	if err := drv.Query(ctx, query, []interface{}{}, rows); err != nil {
   259  		t.Fatalf("unexpected query failure: %q: %v", query, err)
   260  	}
   261  	var dest []interface{}
   262  	for rows.Next() {
   263  		var v interface{}
   264  		if err := rows.Scan(&v); err != nil {
   265  			t.Fatal("unexpected Rows.Scan failure:", err)
   266  		}
   267  		dest = append(dest, v)
   268  	}
   269  	if len(dest) != len(args) {
   270  		t.Fatalf("mismatch rows length: %d != %d", len(dest), len(args))
   271  	}
   272  	for i := range dest {
   273  		if dest[i] != args[i] {
   274  			t.Fatalf("mismatch values: %v %T != %v %T", dest[i], dest[i], args[i], args[i])
   275  		}
   276  	}
   277  	if err := rows.Close(); err != nil {
   278  		t.Fatal(err)
   279  	}
   280  }