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