Repository: upper/db
Pull request #713 (2024-06-23 19:15:50)

test suite maintenance


Patch:
diff --git a/Makefile b/Makefile
index 5216760..eea8429 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,9 @@
-SHELL                 ?= /bin/bash
+SHELL                 ?= bash
+CPU_CORES             ?= $(shell nproc)
 
-PARALLEL_FLAGS        ?= --halt-on-error 2 --jobs=2 -v -u
+PARALLEL_FLAGS        ?= --halt-on-error 2 --jobs=$(CPU_CORES) -v -u
 
-TEST_FLAGS            ?=
+TEST_FLAGS            ?= -v -failfast -race -timeout 20m
 
 UPPER_DB_LOG          ?= WARN
 
diff --git a/adapter/cockroachdb/Makefile b/adapter/cockroachdb/Makefile
index 2c15af3..a6ba132 100644
--- a/adapter/cockroachdb/Makefile
+++ b/adapter/cockroachdb/Makefile
@@ -1,8 +1,8 @@
 SHELL                 ?= bash
 
-COCKROACHDB_VERSION   ?= v23.1.8
-COCKROACHDB_SUPPORTED ?= $(COCKROACHDB_VERSION) v22.2.9 v21.2.9
-PROJECT               ?= upper_cockroachdb_$(COCKROACHDB_VERSION)
+COCKROACHDB_VERSION   ?= v23.2.6
+COCKROACHDB_SUPPORTED ?= $(COCKROACHDB_VERSION) v22.2.19 v21.2.17
+PROJECT               ?= $(subst .,_,"upper_cockroachdb_$(COCKROACHDB_VERSION)")
 
 DB_HOST               ?= 127.0.0.1
 DB_PORT               ?= 26257
@@ -30,7 +30,7 @@ test-no-race:
 	go test -v -failfast $(TEST_FLAGS)
 
 server-up: server-down
-	docker-compose -p $(PROJECT) up -d && \
+	docker compose -p $(PROJECT) up -d && \
 	sleep 5 && \
 	psql -Uroot -h$(DB_HOST) -p$(DB_PORT) postgres -c "\
 		DROP USER IF EXISTS $(DB_USERNAME); \
@@ -41,7 +41,7 @@ server-up: server-down
 	"
 
 server-down:
-	docker-compose -p $(PROJECT) down
+	docker compose -p $(PROJECT) down
 
 test-extended:
 	parallel $(PARALLEL_FLAGS) \
diff --git a/adapter/cockroachdb/connection.go b/adapter/cockroachdb/connection.go
index 04ba8af..bfab6af 100644
--- a/adapter/cockroachdb/connection.go
+++ b/adapter/cockroachdb/connection.go
@@ -75,14 +75,14 @@ func (vs values) Isset(k string) bool {
 //
 // You can use a ConnectionURL struct as an argument for Open:
 //
-//   var settings = cockroachdb.ConnectionURL{
-//     Host:       "localhost",          // Database server IP or host name.
-//     Database:   "peanuts",            // Database name.
-//     User:       "cbrown",             // Optional user name.
-//     Password:   "snoopy",             // Optional user password.
-//   }
+//	var settings = cockroachdb.ConnectionURL{
+//	  Host:       "localhost",          // Database server IP or host name.
+//	  Database:   "peanuts",            // Database name.
+//	  User:       "cbrown",             // Optional user name.
+//	  Password:   "snoopy",             // Optional user password.
+//	}
 //
-//   sess, err = cockroachdb.Open(settings)
+//	sess, err = cockroachdb.Open(settings)
 //
 // If you already have a valid DSN, you can use ParseURL to convert it into
 // a ConnectionURL before passing it to Open.
diff --git a/adapter/cockroachdb/custom_types.go b/adapter/cockroachdb/custom_types.go
index af5d528..6f246e1 100644
--- a/adapter/cockroachdb/custom_types.go
+++ b/adapter/cockroachdb/custom_types.go
@@ -27,6 +27,7 @@ import (
 	"database/sql/driver"
 	"time"
 
+	"github.com/upper/db/v4"
 	"github.com/upper/db/v4/internal/sqlbuilder"
 )
 
@@ -90,12 +91,12 @@ func DecodeJSONB(dst interface{}, src interface{}) error {
 //
 // Example:
 //
-//   type MyCustomStruct struct {
-//     ID int64 `db:"id" json:"id"`
-//     Name string `db:"name" json:"name"`
-//     ...
-//     cockroachdb.JSONBConverter
-//   }
+//	type MyCustomStruct struct {
+//	  ID int64 `db:"id" json:"id"`
+//	  Name string `db:"name" json:"name"`
+//	  ...
+//	  cockroachdb.JSONBConverter
+//	}
 type JSONBConverter struct {
 }
 
@@ -144,7 +145,7 @@ func (t *timeWrapper) Scan(src interface{}) error {
 }
 
 func (d *database) ConvertValueContext(ctx context.Context, in interface{}) interface{} {
-	tz, _ := ctx.Value("timezone").(*time.Location)
+	tz, _ := ctx.Value(db.ContextKey("timezone")).(*time.Location)
 
 	switch v := in.(type) {
 	case *time.Time:
diff --git a/adapter/cockroachdb/custom_types_pgx.go b/adapter/cockroachdb/custom_types_pgx.go
index 42be2a5..2d96958 100644
--- a/adapter/cockroachdb/custom_types_pgx.go
+++ b/adapter/cockroachdb/custom_types_pgx.go
@@ -1,3 +1,4 @@
+//go:build !pq
 // +build !pq
 
 // Copyright (c) 2012-present The upper.io/db authors. All rights reserved.
diff --git a/adapter/cockroachdb/custom_types_pq.go b/adapter/cockroachdb/custom_types_pq.go
index f843e95..92cdd7b 100644
--- a/adapter/cockroachdb/custom_types_pq.go
+++ b/adapter/cockroachdb/custom_types_pq.go
@@ -1,3 +1,4 @@
+//go:build pq
 // +build pq
 
 // Copyright (c) 2012-present The upper.io/db authors. All rights reserved.
diff --git a/adapter/cockroachdb/database_pgx.go b/adapter/cockroachdb/database_pgx.go
index a4aa0d7..102a1f5 100644
--- a/adapter/cockroachdb/database_pgx.go
+++ b/adapter/cockroachdb/database_pgx.go
@@ -1,3 +1,4 @@
+//go:build !pq
 // +build !pq
 
 // Copyright (c) 2012-present The upper.io/db authors. All rights reserved.
@@ -26,9 +27,12 @@ package cockroachdb
 import (
 	"context"
 	"database/sql"
+
+	"time"
+
 	_ "github.com/jackc/pgx/v4/stdlib"
+	"github.com/upper/db/v4"
 	"github.com/upper/db/v4/internal/sqladapter"
-	"time"
 )
 
 func (*database) OpenDSN(sess sqladapter.Session, dsn string) (*sql.DB, error) {
@@ -38,7 +42,7 @@ func (*database) OpenDSN(sess sqladapter.Session, dsn string) (*sql.DB, error) {
 	}
 	if tz := connURL.Options["timezone"]; tz != "" {
 		loc, _ := time.LoadLocation(tz)
-		ctx := context.WithValue(sess.Context(), "timezone", loc)
+		ctx := context.WithValue(sess.Context(), db.ContextKey("timezone"), loc)
 		sess.SetContext(ctx)
 	}
 	return sql.Open("pgx", dsn)
diff --git a/adapter/cockroachdb/database_pq.go b/adapter/cockroachdb/database_pq.go
index 0540e77..ea42c06 100644
--- a/adapter/cockroachdb/database_pq.go
+++ b/adapter/cockroachdb/database_pq.go
@@ -1,3 +1,4 @@
+//go:build pq
 // +build pq
 
 // Copyright (c) 2012-present The upper.io/db authors. All rights reserved.
@@ -26,9 +27,11 @@ package cockroachdb
 import (
 	"context"
 	"database/sql"
+	"time"
+
 	_ "github.com/lib/pq"
+	db "github.com/upper/db/v4"
 	"github.com/upper/db/v4/internal/sqladapter"
-	"time"
 )
 
 func (*database) OpenDSN(sess sqladapter.Session, dsn string) (*sql.DB, error) {
@@ -38,7 +41,7 @@ func (*database) OpenDSN(sess sqladapter.Session, dsn string) (*sql.DB, error) {
 	}
 	if tz := connURL.Options["timezone"]; tz != "" {
 		loc, _ := time.LoadLocation(tz)
-		ctx := context.WithValue(sess.Context(), "timezone", loc)
+		ctx := context.WithValue(sess.Context(), db.ContextKey("timezone"), loc)
 		sess.SetContext(ctx)
 	}
 	return sql.Open("postgres", dsn)
diff --git a/adapter/cockroachdb/docker-compose.yml b/adapter/cockroachdb/docker-compose.yml
index f9f022f..d59e86a 100644
--- a/adapter/cockroachdb/docker-compose.yml
+++ b/adapter/cockroachdb/docker-compose.yml
@@ -1,7 +1,4 @@
-version: '3'
-
 services:
-
   server:
     image: cockroachdb/cockroach:${COCKROACHDB_VERSION:-v2}
     environment:
diff --git a/adapter/mongo/Makefile b/adapter/mongo/Makefile
index 7660d30..fe58bac 100644
--- a/adapter/mongo/Makefile
+++ b/adapter/mongo/Makefile
@@ -2,7 +2,7 @@ SHELL					        ?= bash
 
 MONGO_VERSION         ?= 4
 MONGO_SUPPORTED       ?= $(MONGO_VERSION) 3
-PROJECT               ?= upper_mongo_$(MONGO_VERSION)
+PROJECT               ?= $(subst .,_,"upper_mongo_$(MONGO_VERSION)")
 
 DB_HOST               ?= 127.0.0.1
 DB_PORT               ?= 27017
@@ -31,11 +31,11 @@ test-no-race:
 	go test -v -failfast $(TEST_FLAGS)
 
 server-up: server-down
-	docker-compose -p $(PROJECT) up -d && \
+	docker compose -p $(PROJECT) up -d && \
 	sleep 10
 
 server-down:
-	docker-compose -p $(PROJECT) down
+	docker compose -p $(PROJECT) down
 
 test-extended:
 	parallel $(PARALLEL_FLAGS) \
diff --git a/adapter/mssql/Makefile b/adapter/mssql/Makefile
index acb1a83..c70e54c 100644
--- a/adapter/mssql/Makefile
+++ b/adapter/mssql/Makefile
@@ -2,7 +2,7 @@ SHELL					        ?= bash
 
 MSSQL_VERSION         ?= 2022-latest
 MSSQL_SUPPORTED       ?= $(MSSQL_VERSION) 2019-latest
-PROJECT               ?= upper_mssql_$(MSSQL_VERSION)
+PROJECT               ?= $(subst .,_,"upper_mssql_$(MSSQL_VERSION)")
 
 DB_HOST               ?= 127.0.0.1
 DB_PORT               ?= 1433
@@ -31,11 +31,11 @@ test-no-race:
 	go test -v -failfast $(TEST_FLAGS)
 
 server-up: server-down
-	docker-compose -p $(PROJECT) up -d && \
+	docker compose -p $(PROJECT) up -d && \
 	sleep 20
 
 server-down:
-	docker-compose -p $(PROJECT) down
+	docker compose -p $(PROJECT) down
 
 test-extended:
 	parallel $(PARALLEL_FLAGS) \
diff --git a/adapter/mysql/Makefile b/adapter/mysql/Makefile
index f3a8759..dcad0e6 100644
--- a/adapter/mysql/Makefile
+++ b/adapter/mysql/Makefile
@@ -1,8 +1,8 @@
 SHELL					        ?= bash
 
-MYSQL_VERSION         ?= 8.1
+MYSQL_VERSION         ?= 8.4
 MYSQL_SUPPORTED       ?= $(MYSQL_VERSION) 5.7
-PROJECT               ?= upper_mysql_$(MYSQL_VERSION)
+PROJECT               ?= $(subst .,_,"upper_mysql_$(MYSQL_VERSION)")
 
 DB_HOST               ?= 127.0.0.1
 DB_PORT               ?= 3306
@@ -31,11 +31,11 @@ test-no-race:
 	go test -v -failfast $(TEST_FLAGS)
 
 server-up: server-down
-	docker-compose -p $(PROJECT) up -d && \
+	docker compose -p $(PROJECT) up -d && \
 	sleep 15
 
 server-down:
-	docker-compose -p $(PROJECT) down
+	docker compose -p $(PROJECT) down
 
 test-extended:
 	parallel $(PARALLEL_FLAGS) \
diff --git a/adapter/mysql/custom_types.go b/adapter/mysql/custom_types.go
index 4b78aff..13f2280 100644
--- a/adapter/mysql/custom_types.go
+++ b/adapter/mysql/custom_types.go
@@ -149,12 +149,12 @@ func DecodeJSON(dst interface{}, src interface{}) error {
 //
 // Example:
 //
-//   type MyCustomStruct struct {
-//     ID int64 `db:"id" json:"id"`
-//     Name string `db:"name" json:"name"`
-//     ...
-//     mysql.JSONConverter
-//   }
+//	type MyCustomStruct struct {
+//	  ID int64 `db:"id" json:"id"`
+//	  Name string `db:"name" json:"name"`
+//	  ...
+//	  mysql.JSONConverter
+//	}
 type JSONConverter struct{}
 
 func (*JSONConverter) ConvertValue(in interface{}) interface {
diff --git a/adapter/mysql/docker-compose.yml b/adapter/mysql/docker-compose.yml
index 18ab349..8e2859b 100644
--- a/adapter/mysql/docker-compose.yml
+++ b/adapter/mysql/docker-compose.yml
@@ -1,7 +1,4 @@
-version: '3'
-
 services:
-
   server:
     image: mysql:${MYSQL_VERSION:-5}
     environment:
diff --git a/adapter/postgresql/Makefile b/adapter/postgresql/Makefile
index c9a0ff2..162ffdd 100644
--- a/adapter/postgresql/Makefile
+++ b/adapter/postgresql/Makefile
@@ -1,9 +1,8 @@
 SHELL					        ?= bash
 
-POSTGRES_VERSION      ?= 15-alpine
-POSTGRES_SUPPORTED    ?= $(POSTGRES_VERSION) 14-alpine 13-alpine 12-alpine
-
-PROJECT               ?= upper_postgres_$(POSTGRES_VERSION)
+POSTGRES_VERSION      ?= 16-alpine
+POSTGRES_SUPPORTED    ?= $(POSTGRES_VERSION) 15-alpine 14-alpine
+PROJECT               ?= $(subst .,_,"upper_postgres_$(POSTGRES_VERSION)")
 
 DB_HOST               ?= 127.0.0.1
 DB_PORT               ?= 5432
@@ -26,17 +25,14 @@ export DB_USERNAME
 export TEST_FLAGS
 
 test:
-	go test -v -failfast -race -timeout 20m $(TEST_FLAGS)
-
-test-no-race:
-	go test -v -failfast $(TEST_FLAGS)
+	go test $(TEST_FLAGS)
 
 server-up: server-down
-	docker-compose -p $(PROJECT) up -d && \
+	docker compose -p $(PROJECT) up -d && \
 	sleep 10
 
 server-down:
-	docker-compose -p $(PROJECT) down
+	docker compose -p $(PROJECT) down
 
 test-extended:
 	parallel $(PARALLEL_FLAGS) \
diff --git a/adapter/postgresql/connection.go b/adapter/postgresql/connection.go
index 4769944..ee7a7cd 100644
--- a/adapter/postgresql/connection.go
+++ b/adapter/postgresql/connection.go
@@ -77,14 +77,14 @@ func (vs values) Isset(k string) bool {
 //
 // You can use a ConnectionURL struct as an argument for Open:
 //
-//   var settings = postgresql.ConnectionURL{
-//     Host:       "localhost",          // PostgreSQL server IP or name.
-//     Database:   "peanuts",            // Database name.
-//     User:       "cbrown",             // Optional user name.
-//     Password:   "snoopy",             // Optional user password.
-//   }
+//	var settings = postgresql.ConnectionURL{
+//	  Host:       "localhost",          // PostgreSQL server IP or name.
+//	  Database:   "peanuts",            // Database name.
+//	  User:       "cbrown",             // Optional user name.
+//	  Password:   "snoopy",             // Optional user password.
+//	}
 //
-//   sess, err = postgresql.Open(settings)
+//	sess, err = postgresql.Open(settings)
 //
 // If you already have a valid DSN, you can use ParseURL to convert it into
 // a ConnectionURL before passing it to Open.
@@ -104,7 +104,7 @@ var escaper = strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
 // ParseURL parses the given DSN into a ConnectionURL struct.
 // A typical PostgreSQL connection URL looks like:
 //
-//   postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full
+//	postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full
 func ParseURL(s string) (u *ConnectionURL, err error) {
 	o := make(values)
 
@@ -260,7 +260,7 @@ func newScanner(s string) *scanner {
 //
 //	"postgres://"
 //
-// This will be blank, causing driver.Open to use all of the defaults
+// # This will be blank, causing driver.Open to use all of the defaults
 //
 // NOTE: vendored/copied from github.com/lib/pq
 func parseURL(uri string) (string, error) {
diff --git a/adapter/postgresql/custom_types.go b/adapter/postgresql/custom_types.go
index d06ee20..ff17855 100644
--- a/adapter/postgresql/custom_types.go
+++ b/adapter/postgresql/custom_types.go
@@ -27,6 +27,7 @@ import (
 	"database/sql/driver"
 	"time"
 
+	"github.com/upper/db/v4"
 	"github.com/upper/db/v4/internal/sqlbuilder"
 )
 
@@ -122,7 +123,7 @@ func (t *timeWrapper) Scan(src interface{}) error {
 }
 
 func (d *database) ConvertValueContext(ctx context.Context, in interface{}) interface{} {
-	tz, _ := ctx.Value("timezone").(*time.Location)
+	tz, _ := ctx.Value(db.ContextKey("timezone")).(*time.Location)
 
 	switch v := in.(type) {
 	case *time.Time:
diff --git a/adapter/postgresql/custom_types_pgx.go b/adapter/postgresql/custom_types_pgx.go
index 3559e6b..2a0b658 100644
--- a/adapter/postgresql/custom_types_pgx.go
+++ b/adapter/postgresql/custom_types_pgx.go
@@ -1,3 +1,4 @@
+//go:build !pq
 // +build !pq
 
 // Copyright (c) 2012-present The upper.io/db authors. All rights reserved.
diff --git a/adapter/postgresql/custom_types_pq.go b/adapter/postgresql/custom_types_pq.go
index 20ef131..0ffdeb3 100644
--- a/adapter/postgresql/custom_types_pq.go
+++ b/adapter/postgresql/custom_types_pq.go
@@ -1,3 +1,4 @@
+//go:build pq
 // +build pq
 
 package postgresql
diff --git a/adapter/postgresql/database_pgx.go b/adapter/postgresql/database_pgx.go
index 954a938..6d5a2f3 100644
--- a/adapter/postgresql/database_pgx.go
+++ b/adapter/postgresql/database_pgx.go
@@ -27,9 +27,11 @@ package postgresql
 import (
 	"context"
 	"database/sql"
+	"time"
+
 	_ "github.com/jackc/pgx/v4/stdlib"
+	"github.com/upper/db/v4"
 	"github.com/upper/db/v4/internal/sqladapter"
-	"time"
 )
 
 func (*database) OpenDSN(sess sqladapter.Session, dsn string) (*sql.DB, error) {
@@ -39,7 +41,7 @@ func (*database) OpenDSN(sess sqladapter.Session, dsn string) (*sql.DB, error) {
 	}
 	if tz := connURL.Options["timezone"]; tz != "" {
 		loc, _ := time.LoadLocation(tz)
-		ctx := context.WithValue(sess.Context(), "timezone", loc)
+		ctx := context.WithValue(sess.Context(), db.ContextKey("timezone"), loc)
 		sess.SetContext(ctx)
 	}
 	return sql.Open("pgx", dsn)
diff --git a/adapter/postgresql/database_pq.go b/adapter/postgresql/database_pq.go
index 7b0c9b7..2415f62 100644
--- a/adapter/postgresql/database_pq.go
+++ b/adapter/postgresql/database_pq.go
@@ -1,3 +1,4 @@
+//go:build pq
 // +build pq
 
 package postgresql
@@ -26,9 +27,11 @@ package postgresql
 import (
 	"context"
 	"database/sql"
+	"time"
+
 	_ "github.com/lib/pq"
+	db "github.com/upper/db/v4"
 	"github.com/upper/db/v4/internal/sqladapter"
-	"time"
 )
 
 func (*database) OpenDSN(sess sqladapter.Session, dsn string) (*sql.DB, error) {
@@ -38,7 +41,7 @@ func (*database) OpenDSN(sess sqladapter.Session, dsn string) (*sql.DB, error) {
 	}
 	if tz := connURL.Options["timezone"]; tz != "" {
 		loc, _ := time.LoadLocation(tz)
-		ctx := context.WithValue(sess.Context(), "timezone", loc)
+		ctx := context.WithValue(sess.Context(), db.ContextKey("timezone"), loc)
 		sess.SetContext(ctx)
 	}
 	return sql.Open("postgres", dsn)
diff --git a/adapter/postgresql/docker-compose.yml b/adapter/postgresql/docker-compose.yml
index 4f4884a..7f751f0 100644
--- a/adapter/postgresql/docker-compose.yml
+++ b/adapter/postgresql/docker-compose.yml
@@ -1,7 +1,4 @@
-version: '3'
-
 services:
-
   server:
     image: postgres:${POSTGRES_VERSION:-11}
     environment:
diff --git a/cond.go b/cond.go
index cd8c070..5895ad5 100644
--- a/cond.go
+++ b/cond.go
@@ -43,20 +43,20 @@ type LogicalOperator = adapter.LogicalOperator
 //
 // Examples:
 //
-//  // Age equals 18.
-//  db.Cond{"age": 18}
+//	// Age equals 18.
+//	db.Cond{"age": 18}
 //
-//  // Age is greater than or equal to 18.
-//  db.Cond{"age >=": 18}
+//	// Age is greater than or equal to 18.
+//	db.Cond{"age >=": 18}
 //
-//  // id is any of the values 1, 2 or 3.
-//  db.Cond{"id IN": []{1, 2, 3}}
+//	// id is any of the values 1, 2 or 3.
+//	db.Cond{"id IN": []{1, 2, 3}}
 //
-//  // Age is lower than 18 (MongoDB syntax)
-//  db.Cond{"age $lt": 18}
+//	// Age is lower than 18 (MongoDB syntax)
+//	db.Cond{"age $lt": 18}
 //
-//  // age > 32 and age < 35
-//  db.Cond{"age >": 32, "age <": 35}
+//	// age > 32 and age < 35
+//	db.Cond{"age >": 32, "age <": 35}
 type Cond map[interface{}]interface{}
 
 // Empty returns false if there are no conditions.
diff --git a/context.go b/context.go
new file mode 100644
index 0000000..8c6192c
--- /dev/null
+++ b/context.go
@@ -0,0 +1,3 @@
+package db
+
+type ContextKey string
diff --git a/db.go b/db.go
index dc882b7..f644d07 100644
--- a/db.go
+++ b/db.go
@@ -24,48 +24,48 @@
 //
 // Install upper/db:
 //
-//  go get github.com/upper/db
+//	go get github.com/upper/db
 //
 // Usage
 //
-//  package main
+//	package main
 //
-//  import (
-//  	"log"
+//	import (
+//		"log"
 //
-//  	"github.com/upper/db/v4/adapter/postgresql" // Imports the postgresql adapter.
-//  )
+//		"github.com/upper/db/v4/adapter/postgresql" // Imports the postgresql adapter.
+//	)
 //
-//  var settings = postgresql.ConnectionURL{
-//  	Database: `booktown`,
-//  	Host:     `demo.upper.io`,
-//  	User:     `demouser`,
-//  	Password: `demop4ss`,
-//  }
+//	var settings = postgresql.ConnectionURL{
+//		Database: `booktown`,
+//		Host:     `demo.upper.io`,
+//		User:     `demouser`,
+//		Password: `demop4ss`,
+//	}
 //
-//  // Book represents a book.
-//  type Book struct {
-//  	ID        uint   `db:"id"`
-//  	Title     string `db:"title"`
-//  	AuthorID  uint   `db:"author_id"`
-//  	SubjectID uint   `db:"subject_id"`
-//  }
+//	// Book represents a book.
+//	type Book struct {
+//		ID        uint   `db:"id"`
+//		Title     string `db:"title"`
+//		AuthorID  uint   `db:"author_id"`
+//		SubjectID uint   `db:"subject_id"`
+//	}
 //
-//  func main() {
-//  	sess, err := postgresql.Open(settings)
-//  	if err != nil {
-//  		log.Fatal(err)
-//  	}
-//  	defer sess.Close()
+//	func main() {
+//		sess, err := postgresql.Open(settings)
+//		if err != nil {
+//			log.Fatal(err)
+//		}
+//		defer sess.Close()
 //
-//  	var books []Book
-//  	if err := sess.Collection("books").Find().OrderBy("title").All(&books); err != nil {
-//  		log.Fatal(err)
-//  	}
+//		var books []Book
+//		if err := sess.Collection("books").Find().OrderBy("title").All(&books); err != nil {
+//			log.Fatal(err)
+//		}
 //
-//  	log.Println("Books:")
-//  	for _, book := range books {
-//  		log.Printf("%q (ID: %d)\n", book.Title, book.ID)
-//  	}
-//  }
+//		log.Println("Books:")
+//		for _, book := range books {
+//			log.Printf("%q (ID: %d)\n", book.Title, book.ID)
+//		}
+//	}
 package db
diff --git a/go.mod b/go.mod
index 9ad8930..7997a9c 100644
--- a/go.mod
+++ b/go.mod
@@ -4,25 +4,24 @@ go 1.15
 
 require (
 	github.com/denisenkom/go-mssqldb v0.12.3
-	github.com/go-sql-driver/mysql v1.7.1
+	github.com/go-sql-driver/mysql v1.8.1
 	github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
 	github.com/google/uuid v1.1.1
 	github.com/ipfs/go-detect-race v0.0.1
-	github.com/jackc/pgconn v1.14.1 // indirect
-	github.com/jackc/pgtype v1.14.0
-	github.com/jackc/pgx/v4 v4.18.1
+	github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
+	github.com/jackc/pgtype v1.14.3
+	github.com/jackc/pgx/v4 v4.18.3
 	github.com/lib/pq v1.10.9
-	github.com/mattn/go-sqlite3 v1.14.17
+	github.com/mattn/go-sqlite3 v1.14.22
 	github.com/segmentio/fasthash v1.0.3
 	github.com/sirupsen/logrus v1.9.3
-	github.com/stretchr/testify v1.8.4
-	golang.org/x/crypto v0.12.0 // indirect
-	golang.org/x/sys v0.12.0 // indirect
-	golang.org/x/text v0.13.0 // indirect
+	github.com/stretchr/testify v1.9.0
+	golang.org/x/crypto v0.24.0 // indirect
 	gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
 	modernc.org/b v1.1.0 // indirect
 	modernc.org/db v1.0.10 // indirect
 	modernc.org/file v1.0.8 // indirect
+	modernc.org/fileutil v1.3.0 // indirect
 	modernc.org/golex v1.1.0 // indirect
 	modernc.org/lldb v1.0.8 // indirect
 	modernc.org/ql v1.4.7
diff --git a/go.sum b/go.sum
index e3ae363..a5371d9 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,5 @@
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
 github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
 github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
@@ -21,6 +23,8 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
 github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
 github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
 github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
+github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
+github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
 github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
@@ -31,6 +35,7 @@ github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei
 github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
 github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
 github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -50,6 +55,8 @@ github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRb
 github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E=
 github.com/jackc/pgconn v1.14.1 h1:smbxIaZA08n6YuxEX1sDyjV/qkbtUtkH20qLkR9MUR4=
 github.com/jackc/pgconn v1.14.1/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E=
+github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
+github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
 github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
 github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
 github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
@@ -68,21 +75,30 @@ github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX
 github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
 github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0=
 github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
+github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
 github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
 github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
 github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
+github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
+github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
 github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
 github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
 github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
 github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
 github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw=
 github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
+github.com/jackc/pgtype v1.14.3 h1:h6W9cPuHsRWQFTWUZMAKMgG5jSwQI0Zurzdvlx3Plus=
+github.com/jackc/pgtype v1.14.3/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA=
 github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
 github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
 github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
 github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
 github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0=
 github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE=
+github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
+github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA=
+github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
 github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
 github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
 github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
@@ -107,8 +123,10 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
 github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
-github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
+github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI=
+github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
+github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
+github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
 github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
 github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
@@ -138,6 +156,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -148,6 +167,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -174,14 +195,23 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
-golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
-golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
+golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
+golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
+golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
+golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
+golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
+golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
+golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
+golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
 golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -192,9 +222,15 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
+golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -214,15 +250,25 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
 golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
+golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
-golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
+golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
+golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
+golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
+golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
+golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -231,9 +277,12 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@@ -245,6 +294,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
 golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
+golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -281,6 +332,8 @@ modernc.org/fileutil v1.1.1/go.mod h1:HdjlliqRHrMAI4nVOvvpYVzVgvRSK7WnoCiG0GUWJN
 modernc.org/fileutil v1.1.2/go.mod h1:HdjlliqRHrMAI4nVOvvpYVzVgvRSK7WnoCiG0GUWJNo=
 modernc.org/fileutil v1.2.0 h1:c7fsfzHf9WfUFXvv/RY9sStAr+VAKXYGKiAhBQQNoT4=
 modernc.org/fileutil v1.2.0/go.mod h1:0rLMFc17WSz6Bm/GtHeme7TOX8pNRhFN2NkfBlOZhrQ=
+modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
+modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
 modernc.org/golex v1.0.5/go.mod h1:pTY7KKjdvZbv2ROjfp6FFX5BXMM9QWZEnmCsl60aCfI=
 modernc.org/golex v1.1.0 h1:dmSaksHMd+y6NkBsRsCShNPRaSNCNH+abrVm5/gZic8=
 modernc.org/golex v1.1.0/go.mod h1:2pVlfqApurXhR1m0N+WDYu6Twnc4QuvO4+U8HnwoiRA=
diff --git a/internal/cache/cache.go b/internal/cache/cache.go
index 80dadac..b429b25 100644
--- a/internal/cache/cache.go
+++ b/internal/cache/cache.go
@@ -31,9 +31,10 @@ const defaultCapacity = 128
 
 // Cache holds a map of volatile key -> values.
 type Cache struct {
+	mu sync.RWMutex
+
 	keys     *list.List
 	items    map[uint64]*list.Element
-	mu       sync.RWMutex
 	capacity int
 }
 
@@ -48,9 +49,11 @@ func NewCacheWithCapacity(capacity int) (*Cache, error) {
 	if capacity < 1 {
 		return nil, errors.New("Capacity must be greater than zero.")
 	}
+
 	c := &Cache{
 		capacity: capacity,
 	}
+
 	c.init()
 	return c, nil
 }
@@ -58,9 +61,11 @@ func NewCacheWithCapacity(capacity int) (*Cache, error) {
 // NewCache initializes a new caching space with default settings.
 func NewCache() *Cache {
 	c, err := NewCacheWithCapacity(defaultCapacity)
+
 	if err != nil {
 		panic(err.Error()) // Should never happen as we're not providing a negative defaultCapacity.
 	}
+
 	return c
 }
 
@@ -112,8 +117,9 @@ func (c *Cache) Write(h Hashable, value interface{}) {
 	for c.keys.Len() > c.capacity {
 		item := c.keys.Remove(c.keys.Back()).(*cacheItem)
 		delete(c.items, item.key)
-		if p, ok := item.value.(HasOnEvict); ok {
-			p.OnEvict()
+
+		if evictor, hasOnEvict := item.value.(HasOnEvict); hasOnEvict {
+			evictor.OnEvict()
 		}
 	}
 }
diff --git a/internal/reflectx/reflect.go b/internal/reflectx/reflect.go
index 888edeb..5ca8cbc 100644
--- a/internal/reflectx/reflect.go
+++ b/internal/reflectx/reflect.go
@@ -3,7 +3,6 @@
 // allows for Go-compatible named attribute access, including accessing embedded
 // struct attributes and the ability to use  functions and struct tags to
 // customize field names.
-//
 package reflectx
 
 import (
@@ -155,7 +154,7 @@ func (m *Mapper) FieldByName(v reflect.Value, name string) reflect.Value {
 	tm := m.TypeMap(v.Type())
 	fi, ok := tm.Names[name]
 	if !ok {
-		return v
+		return reflect.Value{}
 	}
 	return FieldByIndexes(v, fi.Index)
 }
diff --git a/internal/sqladapter/collection.go b/internal/sqladapter/collection.go
index f70d0c9..4007816 100644
--- a/internal/sqladapter/collection.go
+++ b/internal/sqladapter/collection.go
@@ -307,7 +307,7 @@ func (c *collectionWithSession) UpdateReturning(item interface{}) error {
 		conds[pk] = db.Eq(sqlbuilder.Mapper.FieldByName(itemValue, pk).Interface())
 	}
 
-	col := tx.(Session).Collection(c.Name())
+	col := tx.Collection(c.Name())
 
 	err = col.Find(conds).Update(item)
 	if err != nil {
diff --git a/internal/sqladapter/compat/query.go b/internal/sqladapter/compat/query.go
index 93cb8fc..91297fb 100644
--- a/internal/sqladapter/compat/query.go
+++ b/internal/sqladapter/compat/query.go
@@ -1,3 +1,4 @@
+//go:build !go1.8
 // +build !go1.8
 
 package compat
diff --git a/internal/sqladapter/compat/query_go18.go b/internal/sqladapter/compat/query_go18.go
index a3abbaf..fbe3890 100644
--- a/internal/sqladapter/compat/query_go18.go
+++ b/internal/sqladapter/compat/query_go18.go
@@ -1,3 +1,4 @@
+//go:build go1.8
 // +build go1.8
 
 package compat
diff --git a/internal/sqladapter/exql/column_value.go b/internal/sqladapter/exql/column_value.go
index 4929611..7ea7d2b 100644
--- a/internal/sqladapter/exql/column_value.go
+++ b/internal/sqladapter/exql/column_value.go
@@ -1,8 +1,9 @@
 package exql
 
 import (
-	"github.com/upper/db/v4/internal/cache"
 	"strings"
+
+	"github.com/upper/db/v4/internal/cache"
 )
 
 // ColumnValue represents a bundle between a column and a corresponding value.
diff --git a/internal/sqladapter/exql/statement.go b/internal/sqladapter/exql/statement.go
index 9b9fd48..5fa009b 100644
--- a/internal/sqladapter/exql/statement.go
+++ b/internal/sqladapter/exql/statement.go
@@ -10,7 +10,7 @@ import (
 
 var errUnknownTemplateType = errors.New("Unknown template type")
 
-//  represents different kinds of SQL statements.
+// represents different kinds of SQL statements.
 type Statement struct {
 	Type
 	Table        Fragment
diff --git a/internal/sqladapter/hash.go b/internal/sqladapter/hash.go
index 4d75491..98fe71d 100644
--- a/internal/sqladapter/hash.go
+++ b/internal/sqladapter/hash.go
@@ -1,7 +1,7 @@
 package sqladapter
 
 const (
-	hashTypeNone = iota + 345065139389
+	_ = iota + 345065139389
 
 	hashTypeCollection
 	hashTypePrimaryKeys
diff --git a/internal/sqladapter/session.go b/internal/sqladapter/session.go
index 0978205..a7c9492 100644
--- a/internal/sqladapter/session.go
+++ b/internal/sqladapter/session.go
@@ -223,8 +223,6 @@ type session struct {
 
 	connURL db.ConnectionURL
 
-	builder db.SQL
-
 	lookupNameOnce sync.Once
 	name           string
 
@@ -1031,7 +1029,7 @@ func ReplaceWithDollarSign(buf []byte) []byte {
 		i = i + 1
 	}
 
-	out = append(out, buf[:len(buf)]...)
+	out = append(out, buf...)
 	buf = nil
 
 	return out
diff --git a/internal/sqlbuilder/builder.go b/internal/sqlbuilder/builder.go
index c3f494c..f54d609 100644
--- a/internal/sqlbuilder/builder.go
+++ b/internal/sqlbuilder/builder.go
@@ -437,7 +437,7 @@ func prepareQueryForDisplay(in string) string {
 		}
 	}
 	if !whitespace {
-		out = append(out, in[offset:len(in)]...)
+		out = append(out, in[offset:]...)
 	}
 	return string(out)
 }
diff --git a/internal/sqlbuilder/convert.go b/internal/sqlbuilder/convert.go
index 21e161f..c1cf218 100644
--- a/internal/sqlbuilder/convert.go
+++ b/internal/sqlbuilder/convert.go
@@ -44,10 +44,10 @@ func expandQuery(in []byte, inArgs []interface{}) ([]byte, []interface{}) {
 		return in, inArgs
 	}
 
-	out = append(out, in[:len(in)]...)
+	out = append(out, in[:]...)
 	in = nil
 
-	outArgs = append(outArgs, inArgs[:len(inArgs)]...)
+	outArgs = append(outArgs, inArgs[:]...)
 	inArgs = nil
 
 	return out, outArgs
diff --git a/internal/sqlbuilder/fetch.go b/internal/sqlbuilder/fetch.go
index fe35dd8..b7fedae 100644
--- a/internal/sqlbuilder/fetch.go
+++ b/internal/sqlbuilder/fetch.go
@@ -26,6 +26,7 @@ import (
 
 	"database/sql"
 	"database/sql/driver"
+
 	db "github.com/upper/db/v4"
 	"github.com/upper/db/v4/internal/reflectx"
 )
diff --git a/internal/sqlbuilder/wrapper.go b/internal/sqlbuilder/wrapper.go
index a16c398..acb6359 100644
--- a/internal/sqlbuilder/wrapper.go
+++ b/internal/sqlbuilder/wrapper.go
@@ -77,7 +77,7 @@ func (d *dbAdapter) Open(conn db.ConnectionURL) (db.Session, error) {
 	if err != nil {
 		return nil, err
 	}
-	return sess.(db.Session), nil
+	return sess, nil
 }
 
 func NewCompatAdapter(adapter Adapter) db.Adapter {
diff --git a/sql.go b/sql.go
index a4bc18b..5aaa2bd 100644
--- a/sql.go
+++ b/sql.go
@@ -33,12 +33,11 @@ import (
 // pointer, if you want to build a query using variables you need to reassign
 // them, like this:
 //
-//  a = builder.Select("name").From("foo") // "a" is created
+//	a = builder.Select("name").From("foo") // "a" is created
 //
-//  a.Where(...) // No effect, the value returned from Where is ignored.
-//
-//  a = a.Where(...) // "a" is reassigned and points to a different address.
+//	a.Where(...) // No effect, the value returned from Where is ignored.
 //
+//	a = a.Where(...) // "a" is reassigned and points to a different address.
 type SQL interface {
 
 	// Select initializes and returns a Selector, it accepts column names as


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 6ddf8e3..d488f9e 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -13,7 +13,7 @@ jobs:
     strategy:
       matrix:
         os: [ubuntu-latest]
-        go-version: [1.19.x, 1.20.x, 1.21.x]
+        go-version: [1.20.x, 1.21.x, 1.22.x]
 
     runs-on: ${{ matrix.os }}
 
diff --git a/adapter/mongo/connection_test.go b/adapter/mongo/connection_test.go
index 4b21726..8dba651 100644
--- a/adapter/mongo/connection_test.go
+++ b/adapter/mongo/connection_test.go
@@ -23,23 +23,20 @@ package mongo
 
 import (
 	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestConnectionURL(t *testing.T) {
-
 	c := ConnectionURL{}
 
 	// Default connection string is only the protocol.
-	if c.String() != "" {
-		t.Fatal(`Expecting default connectiong string to be empty, got:`, c.String())
-	}
+	assert.Equal(t, "", c.String(), "Expecting default connectiong string to be empty")
 
 	// Adding a database name.
 	c.Database = "myfilename"
-
-	if c.String() != "mongodb://myfilename" {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
+	assert.Equal(t, "mongodb://myfilename", c.String())
 
 	// Adding an option.
 	c.Options = map[string]string{
@@ -54,31 +51,22 @@ func TestConnectionURL(t *testing.T) {
 	// Setting host.
 	c.Host = "localhost"
 
-	if c.String() != "mongodb://user:pass@localhost/myfilename?cache=foobar&mode=ro" {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
+	assert.Equal(t, "mongodb://user:pass@localhost/myfilename?cache=foobar&mode=ro", c.String())
 
 	// Setting host and port.
 	c.Host = "localhost:27017"
 
-	if c.String() != "mongodb://user:pass@localhost:27017/myfilename?cache=foobar&mode=ro" {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
+	assert.Equal(t, "mongodb://user:pass@localhost:27017/myfilename?cache=foobar&mode=ro", c.String())
 
 	// Setting cluster.
 	c.Host = "localhost,1.2.3.4,example.org:1234"
 
-	if c.String() != "mongodb://user:pass@localhost,1.2.3.4,example.org:1234/myfilename?cache=foobar&mode=ro" {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
+	assert.Equal(t, "mongodb://user:pass@localhost,1.2.3.4,example.org:1234/myfilename?cache=foobar&mode=ro", c.String())
 
 	// Setting another database.
 	c.Database = "another_database"
 
-	if c.String() != "mongodb://user:pass@localhost,1.2.3.4,example.org:1234/another_database?cache=foobar&mode=ro" {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
-
+	assert.Equal(t, "mongodb://user:pass@localhost,1.2.3.4,example.org:1234/another_database?cache=foobar&mode=ro", c.String())
 }
 
 func TestParseConnectionURL(t *testing.T) {
@@ -88,48 +76,24 @@ func TestParseConnectionURL(t *testing.T) {
 
 	s = "mongodb:///mydatabase"
 
-	if u, err = ParseURL(s); err != nil {
-		t.Fatal(err)
-	}
+	u, err = ParseURL(s)
+	require.NoError(t, err)
 
-	if u.Database != "mydatabase" {
-		t.Fatal("Failed to parse database.")
-	}
+	assert.Equal(t, "mydatabase", u.Database)
 
 	s = "mongodb://user:pass@localhost,1.2.3.4,example.org:1234/another_database?cache=foobar&mode=ro"
 
-	if u, err = ParseURL(s); err != nil {
-		t.Fatal(err)
-	}
+	u, err = ParseURL(s)
+	require.NoError(t, err)
 
-	if u.Database != "another_database" {
-		t.Fatal("Failed to get database.")
-	}
-
-	if u.Options["cache"] != "foobar" {
-		t.Fatal("Expecting option.")
-	}
-
-	if u.Options["mode"] != "ro" {
-		t.Fatal("Expecting option.")
-	}
-
-	if u.User != "user" {
-		t.Fatal("Expecting user.")
-	}
-
-	if u.Password != "pass" {
-		t.Fatal("Expecting password.")
-	}
-
-	if u.Host != "localhost,1.2.3.4,example.org:1234" {
-		t.Fatal("Expecting host.")
-	}
+	assert.Equal(t, "another_database", u.Database)
+	assert.Equal(t, "foobar", u.Options["cache"])
+	assert.Equal(t, "ro", u.Options["mode"])
+	assert.Equal(t, "user", u.User)
+	assert.Equal(t, "pass", u.Password)
+	assert.Equal(t, "localhost,1.2.3.4,example.org:1234", u.Host)
 
 	s = "http://example.org"
-
-	if _, err = ParseURL(s); err == nil {
-		t.Fatal("Expecting error.")
-	}
-
+	_, err = ParseURL(s)
+	require.Error(t, err)
 }
diff --git a/adapter/mssql/connection_test.go b/adapter/mssql/connection_test.go
index 9b1533a..ef9b451 100644
--- a/adapter/mssql/connection_test.go
+++ b/adapter/mssql/connection_test.go
@@ -23,23 +23,20 @@ package mssql
 
 import (
 	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestConnectionURL(t *testing.T) {
-
 	c := ConnectionURL{}
 
 	// Zero value equals to an empty string.
-	if c.String() != "" {
-		t.Fatal(`Expecting default connectiong string to be empty, got:`, c.String())
-	}
+	assert.Equal(t, "", c.String(), "Expecting default connectiong string to be empty")
 
 	// Adding a database name.
 	c.Database = "mydbname"
-
-	if c.String() != "sqlserver://127.0.0.1?database=mydbname" {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
+	assert.Equal(t, "sqlserver://127.0.0.1?database=mydbname", c.String())
 
 	// Adding an option.
 	c.Options = map[string]string{
@@ -48,9 +45,7 @@ func TestConnectionURL(t *testing.T) {
 		"instance":           "instance1",
 	}
 
-	if c.String() != "sqlserver://127.0.0.1/instance1?connection+timeout=30&database=mydbname&param1=value1" {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
+	assert.Equal(t, "sqlserver://127.0.0.1/instance1?connection+timeout=30&database=mydbname&param1=value1", c.String())
 
 	// Setting default options
 	c.Options = nil
@@ -59,16 +54,11 @@ func TestConnectionURL(t *testing.T) {
 	c.User = "user"
 	c.Password = "pass"
 
-	if c.String() != `sqlserver://user:pass@127.0.0.1?database=mydbname` {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
+	assert.Equal(t, `sqlserver://user:pass@127.0.0.1?database=mydbname`, c.String())
 
 	// Setting host.
 	c.Host = "1.2.3.4:1433"
-
-	if c.String() != `sqlserver://user:pass@1.2.3.4:1433?database=mydbname` {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
+	assert.Equal(t, `sqlserver://user:pass@1.2.3.4:1433?database=mydbname`, c.String())
 }
 
 func TestParseConnectionURL(t *testing.T) {
@@ -78,23 +68,11 @@ func TestParseConnectionURL(t *testing.T) {
 
 	s = "sqlserver://user:pass@127.0.0.1:1433?connection+timeout=30&database=mydbname&param1=value1"
 
-	if u, err = ParseURL(s); err != nil {
-		t.Fatal(err)
-	}
-
-	if u.User != "user" {
-		t.Fatal("Expecting username.")
-	}
-
-	if u.Password != "pass" {
-		t.Fatal("Expecting password.")
-	}
-
-	if u.Host != "127.0.0.1:1433" {
-		t.Fatal("Expecting host.")
-	}
+	u, err = ParseURL(s)
+	require.NoError(t, err)
 
-	if u.Database != "mydbname" {
-		t.Fatal("Expecting database.")
-	}
+	assert.Equal(t, "user", u.User)
+	assert.Equal(t, "pass", u.Password)
+	assert.Equal(t, "127.0.0.1:1433", u.Host)
+	assert.Equal(t, "mydbname", u.Database)
 }
diff --git a/adapter/mysql/connection_test.go b/adapter/mysql/connection_test.go
index 894e421..ca1a3a1 100644
--- a/adapter/mysql/connection_test.go
+++ b/adapter/mysql/connection_test.go
@@ -23,6 +23,9 @@ package mysql
 
 import (
 	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestConnectionURL(t *testing.T) {
@@ -30,26 +33,19 @@ func TestConnectionURL(t *testing.T) {
 	c := ConnectionURL{}
 
 	// Zero value equals to an empty string.
-	if c.String() != "" {
-		t.Fatal(`Expecting default connectiong string to be empty, got:`, c.String())
-	}
+	assert.Equal(t, "", c.String(), "Expecting default connectiong string to be empty")
 
 	// Adding a database name.
 	c.Database = "mydbname"
+	assert.Equal(t, "/mydbname?charset=utf8&parseTime=true", c.String())
 
-	if c.String() != "/mydbname?charset=utf8&parseTime=true" {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
-
-	// Adding an option.
+	// Adding options
 	c.Options = map[string]string{
 		"charset": "utf8mb4,utf8",
 		"sys_var": "esc@ped",
 	}
 
-	if c.String() != "/mydbname?charset=utf8mb4%2Cutf8&parseTime=true&sys_var=esc%40ped" {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
+	assert.Equal(t, "/mydbname?charset=utf8mb4%2Cutf8&parseTime=true&sys_var=esc%40ped", c.String())
 
 	// Setting default options
 	c.Options = nil
@@ -58,24 +54,17 @@ func TestConnectionURL(t *testing.T) {
 	c.User = "user"
 	c.Password = "pass"
 
-	if c.String() != `user:pass@/mydbname?charset=utf8&parseTime=true` {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
+	assert.Equal(t, "user:pass@/mydbname?charset=utf8&parseTime=true", c.String())
 
 	// Setting host.
 	c.Host = "1.2.3.4:3306"
 
-	if c.String() != `user:pass@tcp(1.2.3.4:3306)/mydbname?charset=utf8&parseTime=true` {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
+	assert.Equal(t, `user:pass@tcp(1.2.3.4:3306)/mydbname?charset=utf8&parseTime=true`, c.String())
 
 	// Setting socket.
 	c.Socket = "/path/to/socket"
 
-	if c.String() != `user:pass@unix(/path/to/socket)/mydbname?charset=utf8&parseTime=true` {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
-
+	assert.Equal(t, `user:pass@unix(/path/to/socket)/mydbname?charset=utf8&parseTime=true`, c.String())
 }
 
 func TestParseConnectionURL(t *testing.T) {
@@ -85,54 +74,22 @@ func TestParseConnectionURL(t *testing.T) {
 
 	s = "user:pass@unix(/path/to/socket)/mydbname?charset=utf8"
 
-	if u, err = ParseURL(s); err != nil {
-		t.Fatal(err)
-	}
-
-	if u.User != "user" {
-		t.Fatal("Expecting username.")
-	}
-
-	if u.Password != "pass" {
-		t.Fatal("Expecting password.")
-	}
-
-	if u.Socket != "/path/to/socket" {
-		t.Fatal("Expecting socket.")
-	}
-
-	if u.Database != "mydbname" {
-		t.Fatal("Expecting database.")
-	}
+	u, err = ParseURL(s)
+	require.NoError(t, err)
 
-	if u.Options["charset"] != "utf8" {
-		t.Fatal("Expecting charset.")
-	}
+	assert.Equal(t, "user", u.User)
+	assert.Equal(t, "pass", u.Password)
+	assert.Equal(t, "/path/to/socket", u.Socket)
+	assert.Equal(t, "mydbname", u.Database)
+	assert.Equal(t, "utf8", u.Options["charset"])
 
 	s = "user:pass@tcp(1.2.3.4:5678)/mydbname?charset=utf8"
-
-	if u, err = ParseURL(s); err != nil {
-		t.Fatal(err)
-	}
-
-	if u.User != "user" {
-		t.Fatal("Expecting username.")
-	}
-
-	if u.Password != "pass" {
-		t.Fatal("Expecting password.")
-	}
-
-	if u.Host != "1.2.3.4:5678" {
-		t.Fatal("Expecting host.")
-	}
-
-	if u.Database != "mydbname" {
-		t.Fatal("Expecting database.")
-	}
-
-	if u.Options["charset"] != "utf8" {
-		t.Fatal("Expecting charset.")
-	}
-
+	u, err = ParseURL(s)
+	require.NoError(t, err)
+
+	assert.Equal(t, "user", u.User)
+	assert.Equal(t, "pass", u.Password)
+	assert.Equal(t, "1.2.3.4:5678", u.Host)
+	assert.Equal(t, "mydbname", u.Database)
+	assert.Equal(t, "utf8", u.Options["charset"])
 }
diff --git a/adapter/postgresql/postgresql_test.go b/adapter/postgresql/postgresql_test.go
index 900277a..182352b 100644
--- a/adapter/postgresql/postgresql_test.go
+++ b/adapter/postgresql/postgresql_test.go
@@ -126,8 +126,6 @@ func (i64a *int64CompatArray) Scan(src interface{}) error {
 	return nil
 }
 
-type uintCompatArray []uintCompat
-
 type AdapterTests struct {
 	testsuite.Suite
 }
diff --git a/adapter/ql/connection_test.go b/adapter/ql/connection_test.go
index cd453e0..dfb5e2e 100644
--- a/adapter/ql/connection_test.go
+++ b/adapter/ql/connection_test.go
@@ -22,9 +22,10 @@
 package ql
 
 import (
-	"github.com/stretchr/testify/assert"
 	"path/filepath"
 	"testing"
+
+	"github.com/stretchr/testify/assert"
 )
 
 func TestConnectionURL(t *testing.T) {
diff --git a/adapter/sqlite/connection_test.go b/adapter/sqlite/connection_test.go
index 7baa810..f55cca7 100644
--- a/adapter/sqlite/connection_test.go
+++ b/adapter/sqlite/connection_test.go
@@ -24,25 +24,23 @@ package sqlite
 import (
 	"path/filepath"
 	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestConnectionURL(t *testing.T) {
 
 	c := ConnectionURL{}
 
-	// Default connection string is only the protocol.
-	if c.String() != "" {
-		t.Fatal(`Expecting default connectiong string to be empty, got:`, c.String())
-	}
+	assert.Equal(t, "", c.String(), "Expecting default connectiong string to be empty")
 
 	// Adding a database name.
 	c.Database = "myfilename"
 
 	absoluteName, _ := filepath.Abs(c.Database)
 
-	if c.String() != "file://"+absoluteName+"?_busy_timeout=10000" {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
+	assert.Equal(t, "file://"+absoluteName+"?_busy_timeout=10000", c.String())
 
 	// Adding an option.
 	c.Options = map[string]string{
@@ -50,17 +48,12 @@ func TestConnectionURL(t *testing.T) {
 		"mode":  "ro",
 	}
 
-	if c.String() != "file://"+absoluteName+"?_busy_timeout=10000&cache=foobar&mode=ro" {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
+	assert.Equal(t, "file://"+absoluteName+"?_busy_timeout=10000&cache=foobar&mode=ro", c.String())
 
 	// Setting another database.
 	c.Database = "/another/database"
 
-	if c.String() != `file:///another/database?_busy_timeout=10000&cache=foobar&mode=ro` {
-		t.Fatal(`Test failed, got:`, c.String())
-	}
-
+	assert.Equal(t, "file:///another/database?_busy_timeout=10000&cache=foobar&mode=ro", c.String())
 }
 
 func TestParseConnectionURL(t *testing.T) {
@@ -70,40 +63,24 @@ func TestParseConnectionURL(t *testing.T) {
 
 	s = "file://mydatabase.db"
 
-	if u, err = ParseURL(s); err != nil {
-		t.Fatal(err)
-	}
+	u, err = ParseURL(s)
+	require.NoError(t, err)
 
-	if u.Database != "mydatabase.db" {
-		t.Fatal("Failed to parse database.")
-	}
+	assert.Equal(t, "mydatabase.db", u.Database)
 
-	if u.Options["cache"] != "shared" {
-		t.Fatal("If not defined, cache should be shared by default.")
-	}
+	assert.Equal(t, "shared", u.Options["cache"])
 
 	s = "file:///path/to/my/database.db?_busy_timeout=10000&mode=ro&cache=foobar"
 
-	if u, err = ParseURL(s); err != nil {
-		t.Fatal(err)
-	}
-
-	if u.Database != "/path/to/my/database.db" {
-		t.Fatal("Failed to parse username.")
-	}
+	u, err = ParseURL(s)
+	require.NoError(t, err)
 
-	if u.Options["cache"] != "foobar" {
-		t.Fatal("Expecting option.")
-	}
+	assert.Equal(t, "/path/to/my/database.db", u.Database)
 
-	if u.Options["mode"] != "ro" {
-		t.Fatal("Expecting option.")
-	}
+	assert.Equal(t, "foobar", u.Options["cache"])
+	assert.Equal(t, "ro", u.Options["mode"])
 
 	s = "http://example.org"
-
-	if _, err = ParseURL(s); err == nil {
-		t.Fatal("Expecting error.")
-	}
-
+	_, err = ParseURL(s)
+	require.Error(t, err)
 }
diff --git a/comparison_test.go b/comparison_test.go
index 3d171fb..6177a8e 100644
--- a/comparison_test.go
+++ b/comparison_test.go
@@ -22,6 +22,7 @@
 package db
 
 import (
+	"fmt"
 	"testing"
 	"time"
 
@@ -29,7 +30,7 @@ import (
 	"github.com/upper/db/v4/internal/adapter"
 )
 
-func TestComparison(t *testing.T) {
+func TestComparisonOperators(t *testing.T) {
 	testTimeVal := time.Now()
 
 	testCases := []struct {
@@ -127,6 +128,8 @@ func TestComparison(t *testing.T) {
 	}
 
 	for i := range testCases {
-		assert.Equal(t, testCases[i].expects, testCases[i].result.Comparison)
+		t.Run(fmt.Sprintf("Case %02d", i), func(t *testing.T) {
+			assert.Equal(t, testCases[i].expects, testCases[i].result.Comparison)
+		})
 	}
 }
diff --git a/cond_test.go b/cond_test.go
index aed0243..8e0ed40 100644
--- a/cond_test.go
+++ b/cond_test.go
@@ -2,68 +2,50 @@ package db
 
 import (
 	"testing"
+
+	"github.com/stretchr/testify/assert"
 )
 
 func TestCond(t *testing.T) {
-	c := Cond{}
-
-	if !c.Empty() {
-		t.Fatal("Cond is empty.")
-	}
-
-	c = Cond{"id": 1}
-	if c.Empty() {
-		t.Fatal("Cond is not empty.")
-	}
-}
+	t.Run("Base", func(t *testing.T) {
+		var c Cond
 
-func TestCondAnd(t *testing.T) {
-	a := And()
+		c = Cond{}
+		assert.True(t, c.Empty())
 
-	if !a.Empty() {
-		t.Fatal("Cond is empty")
-	}
+		c = Cond{"id": 1}
+		assert.False(t, c.Empty())
+	})
 
-	_ = a.And(Cond{"id": 1})
+	t.Run("And", func(t *testing.T) {
+		var a *AndExpr
 
-	if !a.Empty() {
-		t.Fatal("Cond is still empty")
-	}
+		a = And()
+		assert.True(t, a.Empty())
 
-	a = a.And(Cond{"name": "Ana"})
-
-	if a.Empty() {
-		t.Fatal("Cond is not empty anymore")
-	}
-
-	a = a.And().And()
-
-	if a.Empty() {
-		t.Fatal("Cond is not empty anymore")
-	}
-}
+		_ = a.And(Cond{"id": 1})
+		assert.True(t, a.Empty(), "conditions are immutable")
 
-func TestCondOr(t *testing.T) {
-	a := Or()
+		a = a.And(Cond{"name": "Ana"})
+		assert.False(t, a.Empty())
 
-	if !a.Empty() {
-		t.Fatal("Cond is empty")
-	}
+		a = a.And().And()
+		assert.False(t, a.Empty())
+	})
 
-	_ = a.Or(Cond{"id": 1})
+	t.Run("Or", func(t *testing.T) {
+		var a *OrExpr
 
-	if !a.Empty() {
-		t.Fatal("Cond is empty")
-	}
+		a = Or()
+		assert.True(t, a.Empty())
 
-	a = a.Or(Cond{"name": "Ana"})
+		_ = a.Or(Cond{"id": 1})
+		assert.True(t, a.Empty(), "conditions are immutable")
 
-	if a.Empty() {
-		t.Fatal("Cond is not empty")
-	}
+		a = a.Or(Cond{"name": "Ana"})
+		assert.False(t, a.Empty())
 
-	a = a.Or().Or()
-	if a.Empty() {
-		t.Fatal("Cond is not empty")
-	}
+		a = a.Or().Or()
+		assert.False(t, a.Empty())
+	})
 }
diff --git a/errors_test.go b/errors_test.go
deleted file mode 100644
index 927b71f..0000000
--- a/errors_test.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2012-present The upper.io/db authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-package db
-
-import (
-	"errors"
-	"fmt"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestErrorWrap(t *testing.T) {
-	adapterFakeErr := fmt.Errorf("could not find item in %q: %w", "users", ErrCollectionDoesNotExist)
-	assert.True(t, errors.Is(adapterFakeErr, ErrCollectionDoesNotExist))
-}
diff --git a/function_test.go b/function_test.go
index 1ffd4a3..bf4f90f 100644
--- a/function_test.go
+++ b/function_test.go
@@ -6,46 +6,46 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestFunction(t *testing.T) {
-	{
-		fn := Func("MOD", 29, 9)
-		assert.Equal(t, "MOD", fn.Name())
-		assert.Equal(t, []interface{}{29, 9}, fn.Arguments())
-	}
-
-	{
+func TestCustomFunctions(t *testing.T) {
+	t.Run("Nil arguments", func(t *testing.T) {
 		fn := Func("HELLO")
 		assert.Equal(t, "HELLO", fn.Name())
 		assert.Equal(t, []interface{}(nil), fn.Arguments())
-	}
+	})
 
-	{
+	t.Run("Single argument", func(t *testing.T) {
 		fn := Func("CONCAT", "a")
 		assert.Equal(t, "CONCAT", fn.Name())
 		assert.Equal(t, []interface{}{"a"}, fn.Arguments())
-	}
+	})
+
+	t.Run("Two arguments", func(t *testing.T) {
+		fn := Func("MOD", 29, 9)
+		assert.Equal(t, "MOD", fn.Name())
+		assert.Equal(t, []interface{}{29, 9}, fn.Arguments())
+	})
 
-	{
+	t.Run("Multiple arguments", func(t *testing.T) {
 		fn := Func("CONCAT", "a", "b", "c")
 		assert.Equal(t, "CONCAT", fn.Name())
 		assert.Equal(t, []interface{}{"a", "b", "c"}, fn.Arguments())
-	}
+	})
 
-	{
+	t.Run("Slice argument", func(t *testing.T) {
 		fn := Func("IN", []interface{}{"a", "b", "c"})
 		assert.Equal(t, "IN", fn.Name())
 		assert.Equal(t, []interface{}{[]interface{}{"a", "b", "c"}}, fn.Arguments())
-	}
+	})
 
-	{
+	t.Run("Slice argument with one element", func(t *testing.T) {
 		fn := Func("IN", []interface{}{"a"})
 		assert.Equal(t, "IN", fn.Name())
 		assert.Equal(t, []interface{}{[]interface{}{"a"}}, fn.Arguments())
-	}
+	})
 
-	{
+	t.Run("Nil slice argument", func(t *testing.T) {
 		fn := Func("IN", []interface{}(nil))
 		assert.Equal(t, "IN", fn.Name())
 		assert.Equal(t, []interface{}{[]interface{}(nil)}, fn.Arguments())
-	}
+	})
 }
diff --git a/internal/cache/cache_test.go b/internal/cache/cache_test.go
index 76634cc..a961507 100644
--- a/internal/cache/cache_test.go
+++ b/internal/cache/cache_test.go
@@ -25,9 +25,9 @@ import (
 	"fmt"
 	"hash/fnv"
 	"testing"
-)
 
-var c *Cache
+	"github.com/stretchr/testify/assert"
+)
 
 type cacheableT struct {
 	Name string
@@ -39,39 +39,34 @@ func (ct *cacheableT) Hash() uint64 {
 	return s.Sum64()
 }
 
-var (
-	key   = cacheableT{"foo"}
-	value = "bar"
-)
-
-func TestNewCache(t *testing.T) {
-	c = NewCache()
-	if c == nil {
-		t.Fatal("Expecting a new cache object.")
-	}
-}
-
-func TestCacheReadNonExistentValue(t *testing.T) {
-	if _, ok := c.Read(&key); ok {
-		t.Fatal("Expecting false.")
-	}
-}
-
-func TestCacheWritingValue(t *testing.T) {
-	c.Write(&key, value)
-	c.Write(&key, value)
-}
-
-func TestCacheReadExistentValue(t *testing.T) {
-	s, ok := c.Read(&key)
-
-	if !ok {
-		t.Fatal("Expecting true.")
-	}
-
-	if s != value {
-		t.Fatal("Expecting value.")
-	}
+func TestCache(t *testing.T) {
+	var c *Cache
+
+	var (
+		key   = cacheableT{"foo"}
+		value = "bar"
+	)
+
+	t.Run("New", func(t *testing.T) {
+		c = NewCache()
+		assert.NotNil(t, c)
+	})
+
+	t.Run("ReadNonExistentValue", func(t *testing.T) {
+		_, ok := c.Read(&key)
+		assert.False(t, ok)
+	})
+
+	t.Run("Write", func(t *testing.T) {
+		c.Write(&key, value)
+		c.Write(&key, value)
+	})
+
+	t.Run("ReadExistentValue", func(t *testing.T) {
+		v, ok := c.Read(&key)
+		assert.True(t, ok)
+		assert.Equal(t, value, v)
+	})
 }
 
 func BenchmarkNewCache(b *testing.B) {
@@ -88,6 +83,8 @@ func BenchmarkNewCacheAndClear(b *testing.B) {
 }
 
 func BenchmarkReadNonExistentValue(b *testing.B) {
+	key := cacheableT{"foo"}
+
 	z := NewCache()
 	for i := 0; i < b.N; i++ {
 		z.Read(&key)
@@ -95,6 +92,9 @@ func BenchmarkReadNonExistentValue(b *testing.B) {
 }
 
 func BenchmarkWriteSameValue(b *testing.B) {
+	key := cacheableT{"foo"}
+	value := "bar"
+
 	z := NewCache()
 	for i := 0; i < b.N; i++ {
 		z.Write(&key, value)
@@ -102,6 +102,8 @@ func BenchmarkWriteSameValue(b *testing.B) {
 }
 
 func BenchmarkWriteNewValue(b *testing.B) {
+	value := "bar"
+
 	z := NewCache()
 	for i := 0; i < b.N; i++ {
 		key := cacheableT{fmt.Sprintf("item-%d", i)}
@@ -110,6 +112,9 @@ func BenchmarkWriteNewValue(b *testing.B) {
 }
 
 func BenchmarkReadExistentValue(b *testing.B) {
+	key := cacheableT{"foo"}
+	value := "bar"
+
 	z := NewCache()
 	z.Write(&key, value)
 	for i := 0; i < b.N; i++ {
diff --git a/internal/reflectx/reflect_test.go b/internal/reflectx/reflect_test.go
index 8072244..e769e47 100644
--- a/internal/reflectx/reflect_test.go
+++ b/internal/reflectx/reflect_test.go
@@ -4,524 +4,400 @@ import (
 	"reflect"
 	"strings"
 	"testing"
+
+	"github.com/stretchr/testify/assert"
 )
 
-func ival(v reflect.Value) int {
-	return v.Interface().(int)
+type E1 struct {
+	A int
+}
+type E2 struct {
+	E1
+	B int
+}
+type E3 struct {
+	E2
+	C int
+}
+type E4 struct {
+	E3
+	D int
 }
 
-func TestBasic(t *testing.T) {
-	type Foo struct {
-		A int
-		B int
-		C int
-	}
+func TestReflectMapper(t *testing.T) {
+	t.Run("TopLevelField", func(t *testing.T) {
+		type A struct {
+			F0 int
+			F1 int
+			F2 int
+		}
 
-	f := Foo{1, 2, 3}
-	fv := reflect.ValueOf(f)
-	m := NewMapperFunc("", func(s string) string { return s })
+		f := A{1, 2, 3}
+		fv := reflect.ValueOf(f)
 
-	v := m.FieldByName(fv, "A")
-	if ival(v) != f.A {
-		t.Errorf("Expecting %d, got %d", ival(v), f.A)
-	}
-	v = m.FieldByName(fv, "B")
-	if ival(v) != f.B {
-		t.Errorf("Expecting %d, got %d", f.B, ival(v))
-	}
-	v = m.FieldByName(fv, "C")
-	if ival(v) != f.C {
-		t.Errorf("Expecting %d, got %d", f.C, ival(v))
-	}
-}
+		m := NewMapperFunc("", func(s string) string { return s })
 
-func TestBasicEmbedded(t *testing.T) {
-	type Foo struct {
-		A int
-	}
+		{
+			v := m.FieldByName(fv, "F0")
+			assert.Equal(t, f.F0, v.Interface().(int))
+		}
 
-	type Bar struct {
-		Foo // `db:""` is implied for an embedded struct
-		B   int
-		C   int `db:"-"`
-	}
+		{
+			v := m.FieldByName(fv, "F2")
+			assert.Equal(t, f.F2, v.Interface().(int))
+		}
+	})
 
-	type Baz struct {
-		A   int
-		Bar `db:"Bar"`
-	}
+	t.Run("NestedFields", func(t *testing.T) {
+		type A struct {
+			F0 int
+			F1 int
+			F2 int
+		}
 
-	m := NewMapperFunc("db", func(s string) string { return s })
+		type B struct {
+			A // nested
 
-	z := Baz{}
-	z.A = 1
-	z.B = 2
-	z.C = 4
-	z.Bar.Foo.A = 3
+			F3 int
+			F4 int
+		}
 
-	zv := reflect.ValueOf(z)
-	fields := m.TypeMap(reflect.TypeOf(z))
+		type C struct {
+			F5 int
 
-	if len(fields.Index) != 5 {
-		t.Errorf("Expecting 5 fields")
-	}
+			B `db:"B"`
+		}
 
-	// for _, fi := range fields.Index {
-	// 	log.Println(fi)
-	// }
+		c := C{1, B{A{2, 3, 4}, 5, 6}}
+		cv := reflect.ValueOf(c)
 
-	v := m.FieldByName(zv, "A")
-	if ival(v) != z.A {
-		t.Errorf("Expecting %d, got %d", z.A, ival(v))
-	}
-	v = m.FieldByName(zv, "Bar.B")
-	if ival(v) != z.Bar.B {
-		t.Errorf("Expecting %d, got %d", z.Bar.B, ival(v))
-	}
-	v = m.FieldByName(zv, "Bar.A")
-	if ival(v) != z.Bar.Foo.A {
-		t.Errorf("Expecting %d, got %d", z.Bar.Foo.A, ival(v))
-	}
-	v = m.FieldByName(zv, "Bar.C")
-	if _, ok := v.Interface().(int); ok {
-		t.Errorf("Expecting Bar.C to not exist")
-	}
+		m := NewMapperFunc("db", func(s string) string { return s })
 
-	fi := fields.GetByPath("Bar.C")
-	if fi != nil {
-		t.Errorf("Bar.C should not exist")
-	}
-}
+		assert.Equal(t, 1, m.FieldByName(cv, "F5").Interface().(int))
+		assert.Equal(t, 2, m.FieldByName(cv, "B.F0").Interface().(int))
+		assert.Equal(t, 3, m.FieldByName(cv, "B.F1").Interface().(int))
+		assert.Equal(t, 4, m.FieldByName(cv, "B.F2").Interface().(int))
+		assert.Equal(t, 5, m.FieldByName(cv, "B.F3").Interface().(int))
+		assert.Equal(t, 6, m.FieldByName(cv, "B.F4").Interface().(int))
 
-func TestEmbeddedSimple(t *testing.T) {
-	type UUID [16]byte
-	type MyID struct {
-		UUID
-	}
-	type Item struct {
-		ID MyID
-	}
-	z := Item{}
+		assert.False(t, m.FieldByName(cv, "D").IsValid())
 
-	m := NewMapper("db")
-	m.TypeMap(reflect.TypeOf(z))
-}
+		t.Run("TypeMap", func(t *testing.T) {
+			fields := m.TypeMap(reflect.TypeOf(c))
 
-func TestBasicEmbeddedWithTags(t *testing.T) {
-	type Foo struct {
-		A int `db:"a"`
-	}
+			assert.Equal(t, 8, len(fields.Index))
+			assert.Equal(t, 1, len(fields.GetByPath("F5").Index))
+			assert.Equal(t, "F5", fields.GetByPath("F5").Name)
+			assert.Zero(t, fields.GetByPath("F6"))
+		})
+	})
 
-	type Bar struct {
-		Foo     // `db:""` is implied for an embedded struct
-		B   int `db:"b"`
-	}
+	t.Run("NestedFieldsWithTags", func(t *testing.T) {
+		m := NewMapper("db")
 
-	type Baz struct {
-		A   int `db:"a"`
-		Bar     // `db:""` is implied for an embedded struct
-	}
+		type Details struct {
+			Active bool `db:"active"`
+		}
 
-	m := NewMapper("db")
+		type Asset struct {
+			Title   string  `db:"title"`
+			Details Details `db:"details"`
+		}
 
-	z := Baz{}
-	z.A = 1
-	z.B = 2
-	z.Bar.Foo.A = 3
+		type Post struct {
+			Author string `db:"author,required"`
+			Asset  `db:"asset"`
+		}
 
-	zv := reflect.ValueOf(z)
-	fields := m.TypeMap(reflect.TypeOf(z))
+		post := Post{
+			Author: "Joe",
+			Asset: Asset{
+				Title: "Hello",
+				Details: Details{
+					Active: true,
+				},
+			},
+		}
 
-	if len(fields.Index) != 5 {
-		t.Errorf("Expecting 5 fields")
-	}
+		pv := reflect.ValueOf(post)
 
-	// for _, fi := range fields.index {
-	// 	log.Println(fi)
-	// }
+		assert.Equal(t, "Joe", m.FieldByName(pv, "author").Interface().(string))
+		assert.Equal(t, "Hello", m.FieldByName(pv, "asset.title").Interface().(string))
+		assert.Zero(t, m.FieldByName(pv, "title"))
+		assert.Equal(t, true, m.FieldByName(pv, "asset.details.active").Interface().(bool))
+	})
 
-	v := m.FieldByName(zv, "a")
-	if ival(v) != z.Bar.Foo.A { // the dominant field
-		t.Errorf("Expecting %d, got %d", z.Bar.Foo.A, ival(v))
-	}
-	v = m.FieldByName(zv, "b")
-	if ival(v) != z.B {
-		t.Errorf("Expecting %d, got %d", z.B, ival(v))
-	}
-}
+	t.Run("NestedFieldsWithAmbiguousTags", func(t *testing.T) {
+		type Foo struct {
+			A int `db:"a"`
+		}
 
-func TestFlatTags(t *testing.T) {
-	m := NewMapper("db")
+		type Bar struct {
+			Foo     // `db:""` is implied for an embedded struct
+			B   int `db:"b"`
+		}
 
-	type Asset struct {
-		Title string `db:"title"`
-	}
-	type Post struct {
-		Author string `db:"author,required"`
-		Asset  Asset  `db:""`
-	}
-	// Post columns: (author title)
+		type Baz struct {
+			A   int `db:"a"`
+			Bar     // `db:""` is implied for an embedded struct
+		}
 
-	post := Post{Author: "Joe", Asset: Asset{Title: "Hello"}}
-	pv := reflect.ValueOf(post)
+		m := NewMapper("db")
 
-	v := m.FieldByName(pv, "author")
-	if v.Interface().(string) != post.Author {
-		t.Errorf("Expecting %s, got %s", post.Author, v.Interface().(string))
-	}
-	v = m.FieldByName(pv, "title")
-	if v.Interface().(string) != post.Asset.Title {
-		t.Errorf("Expecting %s, got %s", post.Asset.Title, v.Interface().(string))
-	}
-}
+		z := Baz{A: 1, Bar: Bar{Foo: Foo{A: 3}, B: 2}}
 
-func TestNestedStruct(t *testing.T) {
-	m := NewMapper("db")
+		zv := reflect.ValueOf(z)
+		fields := m.TypeMap(reflect.TypeOf(z))
 
-	type Details struct {
-		Active bool `db:"active"`
-	}
-	type Asset struct {
-		Title   string  `db:"title"`
-		Details Details `db:"details"`
-	}
-	type Post struct {
-		Author string `db:"author,required"`
-		Asset  `db:"asset"`
-	}
-	// Post columns: (author asset.title asset.details.active)
+		assert.Equal(t, 5, len(fields.Index))
 
-	post := Post{
-		Author: "Joe",
-		Asset:  Asset{Title: "Hello", Details: Details{Active: true}},
-	}
-	pv := reflect.ValueOf(post)
+		assert.Equal(t, 3, m.FieldByName(zv, "a").Interface().(int))
+		assert.Equal(t, 2, m.FieldByName(zv, "b").Interface().(int))
+	})
 
-	v := m.FieldByName(pv, "author")
-	if v.Interface().(string) != post.Author {
-		t.Errorf("Expecting %s, got %s", post.Author, v.Interface().(string))
-	}
-	v = m.FieldByName(pv, "title")
-	if _, ok := v.Interface().(string); ok {
-		t.Errorf("Expecting field to not exist")
-	}
-	v = m.FieldByName(pv, "asset.title")
-	if v.Interface().(string) != post.Asset.Title {
-		t.Errorf("Expecting %s, got %s", post.Asset.Title, v.Interface().(string))
-	}
-	v = m.FieldByName(pv, "asset.details.active")
-	if v.Interface().(bool) != post.Asset.Details.Active {
-		t.Errorf("Expecting %v, got %v", post.Asset.Details.Active, v.Interface().(bool))
-	}
-}
+	t.Run("InlineStructs", func(t *testing.T) {
+		m := NewMapperTagFunc("db", strings.ToLower, nil)
 
-func TestInlineStruct(t *testing.T) {
-	m := NewMapperTagFunc("db", strings.ToLower, nil)
+		type Employee struct {
+			Name string
+			ID   int
+		}
 
-	type Employee struct {
-		Name string
-		ID   int
-	}
-	type Boss Employee
-	type person struct {
-		Employee `db:"employee"`
-		Boss     `db:"boss"`
-	}
-	// employees columns: (employee.name employee.id boss.name boss.id)
+		type Boss Employee
 
-	em := person{Employee: Employee{Name: "Joe", ID: 2}, Boss: Boss{Name: "Dick", ID: 1}}
-	ev := reflect.ValueOf(em)
+		type person struct {
+			Employee `db:"employee"`
+			Boss     `db:"boss"`
+		}
 
-	fields := m.TypeMap(reflect.TypeOf(em))
-	if len(fields.Index) != 6 {
-		t.Errorf("Expecting 6 fields")
-	}
+		em := person{
+			Employee: Employee{
+				Name: "Joe",
+				ID:   2,
+			},
+			Boss: Boss{
+				Name: "Rick",
+				ID:   1,
+			},
+		}
+		ev := reflect.ValueOf(em)
 
-	v := m.FieldByName(ev, "employee.name")
-	if v.Interface().(string) != em.Employee.Name {
-		t.Errorf("Expecting %s, got %s", em.Employee.Name, v.Interface().(string))
-	}
-	v = m.FieldByName(ev, "boss.id")
-	if ival(v) != em.Boss.ID {
-		t.Errorf("Expecting %v, got %v", em.Boss.ID, ival(v))
-	}
-}
+		fields := m.TypeMap(reflect.TypeOf(em))
 
-func TestFieldsEmbedded(t *testing.T) {
-	m := NewMapper("db")
+		assert.Equal(t, 6, len(fields.Index))
 
-	type Person struct {
-		Name string `db:"name"`
-	}
-	type Place struct {
-		Name string `db:"name"`
-	}
-	type Article struct {
-		Title string `db:"title"`
-	}
-	type PP struct {
-		Person  `db:"person,required"`
-		Place   `db:",someflag"`
-		Article `db:",required"`
-	}
-	// PP columns: (person.name name title)
+		assert.Equal(t, "Joe", m.FieldByName(ev, "employee.name").Interface().(string))
+		assert.Equal(t, 2, m.FieldByName(ev, "employee.id").Interface().(int))
+		assert.Equal(t, "Rick", m.FieldByName(ev, "boss.name").Interface().(string))
+		assert.Equal(t, 1, m.FieldByName(ev, "boss.id").Interface().(int))
+	})
 
-	pp := PP{}
-	pp.Person.Name = "Peter"
-	pp.Place.Name = "Toronto"
-	pp.Article.Title = "Best city ever"
+	t.Run("FieldsWithTags", func(t *testing.T) {
+		m := NewMapper("db")
 
-	fields := m.TypeMap(reflect.TypeOf(pp))
-	// for i, f := range fields {
-	// 	log.Println(i, f)
-	// }
+		type Person struct {
+			Name string `db:"name"`
+		}
 
-	ppv := reflect.ValueOf(pp)
+		type Place struct {
+			Name string `db:"name"`
+		}
 
-	v := m.FieldByName(ppv, "person.name")
-	if v.Interface().(string) != pp.Person.Name {
-		t.Errorf("Expecting %s, got %s", pp.Person.Name, v.Interface().(string))
-	}
+		type Article struct {
+			Title string `db:"title"`
+		}
 
-	v = m.FieldByName(ppv, "name")
-	if v.Interface().(string) != pp.Place.Name {
-		t.Errorf("Expecting %s, got %s", pp.Place.Name, v.Interface().(string))
-	}
+		type PP struct {
+			Person  `db:"person,required"`
+			Place   `db:",someflag"`
+			Article `db:",required"`
+		}
 
-	v = m.FieldByName(ppv, "title")
-	if v.Interface().(string) != pp.Article.Title {
-		t.Errorf("Expecting %s, got %s", pp.Article.Title, v.Interface().(string))
-	}
+		pp := PP{
+			Person: Person{
+				Name: "Peter",
+			},
+			Place: Place{
+				Name: "Toronto",
+			},
+			Article: Article{
+				Title: "Best city ever",
+			},
+		}
 
-	fi := fields.GetByPath("person")
-	if _, ok := fi.Options["required"]; !ok {
-		t.Errorf("Expecting required option to be set")
-	}
-	if !fi.Embedded {
-		t.Errorf("Expecting field to be embedded")
-	}
-	if len(fi.Index) != 1 || fi.Index[0] != 0 {
-		t.Errorf("Expecting index to be [0]")
-	}
+		ppv := reflect.ValueOf(pp)
+		fields := m.TypeMap(reflect.TypeOf(pp))
 
-	fi = fields.GetByPath("person.name")
-	if fi == nil {
-		t.Errorf("Expecting person.name to exist")
-	}
-	if fi.Path != "person.name" {
-		t.Errorf("Expecting %s, got %s", "person.name", fi.Path)
-	}
+		v := m.FieldByName(ppv, "person.name")
 
-	fi = fields.GetByTraversal([]int{1, 0})
-	if fi == nil {
-		t.Errorf("Expecting traveral to exist")
-	}
-	if fi.Path != "name" {
-		t.Errorf("Expecting %s, got %s", "name", fi.Path)
-	}
+		assert.Equal(t, "Peter", v.Interface().(string))
+		assert.Equal(t, "Toronto", m.FieldByName(ppv, "name").Interface().(string))
+		assert.Equal(t, "Best city ever", m.FieldByName(ppv, "title").Interface().(string))
 
-	fi = fields.GetByTraversal([]int{2})
-	if fi == nil {
-		t.Errorf("Expecting traversal to exist")
-	}
-	if _, ok := fi.Options["required"]; !ok {
-		t.Errorf("Expecting required option to be set")
-	}
+		fi := fields.GetByPath("person")
 
-	trs := m.TraversalsByName(reflect.TypeOf(pp), []string{"person.name", "name", "title"})
-	if !reflect.DeepEqual(trs, [][]int{{0, 0}, {1, 0}, {2, 0}}) {
-		t.Errorf("Expecting traversal: %v", trs)
-	}
-}
+		{
+			_, ok := fi.Options["required"]
+			assert.True(t, ok)
 
-func TestPtrFields(t *testing.T) {
-	m := NewMapperTagFunc("db", strings.ToLower, nil)
-	type Asset struct {
-		Title string
-	}
-	type Post struct {
-		*Asset `db:"asset"`
-		Author string
-	}
+			assert.Zero(t, fi.Options["required"])
+		}
+		assert.True(t, fi.Embedded)
 
-	post := &Post{Author: "Joe", Asset: &Asset{Title: "Hiyo"}}
-	pv := reflect.ValueOf(post)
+		assert.Len(t, fi.Index, 1)
+		assert.Equal(t, 0, fi.Index[0])
 
-	fields := m.TypeMap(reflect.TypeOf(post))
-	if len(fields.Index) != 3 {
-		t.Errorf("Expecting 3 fields")
-	}
+		assert.Equal(t, "person.name", fields.GetByPath("person.name").Path)
 
-	v := m.FieldByName(pv, "asset.title")
-	if v.Interface().(string) != post.Asset.Title {
-		t.Errorf("Expecting %s, got %s", post.Asset.Title, v.Interface().(string))
-	}
-	v = m.FieldByName(pv, "author")
-	if v.Interface().(string) != post.Author {
-		t.Errorf("Expecting %s, got %s", post.Author, v.Interface().(string))
-	}
-}
+		assert.Equal(t, "name", fields.GetByTraversal([]int{1, 0}).Path)
 
-func TestNamedPtrFields(t *testing.T) {
-	m := NewMapperTagFunc("db", strings.ToLower, nil)
+		fi = fields.GetByTraversal([]int{2})
+		assert.NotNil(t, fi)
 
-	type User struct {
-		Name string
-	}
+		_, ok := fi.Options["required"]
+		assert.True(t, ok)
 
-	type Asset struct {
-		Title string
+		trs := m.TraversalsByName(reflect.TypeOf(pp), []string{"person.name", "name", "title"})
+		assert.Equal(t, [][]int{{0, 0}, {1, 0}, {2, 0}}, trs)
+	})
 
-		Owner *User `db:"owner"`
-	}
-	type Post struct {
-		Author string
+	t.Run("PointerFields", func(t *testing.T) {
+		m := NewMapperTagFunc("db", strings.ToLower, nil)
 
-		Asset1 *Asset `db:"asset1"`
-		Asset2 *Asset `db:"asset2"`
-	}
+		type Asset struct {
+			Title string
+		}
 
-	post := &Post{Author: "Joe", Asset1: &Asset{Title: "Hiyo", Owner: &User{"Username"}}} // Let Asset2 be nil
-	pv := reflect.ValueOf(post)
+		type Post struct {
+			*Asset `db:"asset"`
+			Author string
+		}
 
-	fields := m.TypeMap(reflect.TypeOf(post))
-	if len(fields.Index) != 9 {
-		t.Errorf("Expecting 9 fields")
-	}
+		post := &Post{
+			Author: "Joe",
+			Asset: &Asset{
+				Title: "Hiyo",
+			},
+		}
 
-	v := m.FieldByName(pv, "asset1.title")
-	if v.Interface().(string) != post.Asset1.Title {
-		t.Errorf("Expecting %s, got %s", post.Asset1.Title, v.Interface().(string))
-	}
-	v = m.FieldByName(pv, "asset1.owner.name")
-	if v.Interface().(string) != post.Asset1.Owner.Name {
-		t.Errorf("Expecting %s, got %s", post.Asset1.Owner.Name, v.Interface().(string))
-	}
-	v = m.FieldByName(pv, "asset2.title")
-	if v.Interface().(string) != post.Asset2.Title {
-		t.Errorf("Expecting %s, got %s", post.Asset2.Title, v.Interface().(string))
-	}
-	v = m.FieldByName(pv, "asset2.owner.name")
-	if v.Interface().(string) != post.Asset2.Owner.Name {
-		t.Errorf("Expecting %s, got %s", post.Asset2.Owner.Name, v.Interface().(string))
-	}
-	v = m.FieldByName(pv, "author")
-	if v.Interface().(string) != post.Author {
-		t.Errorf("Expecting %s, got %s", post.Author, v.Interface().(string))
-	}
-}
+		pv := reflect.ValueOf(post)
 
-func TestFieldMap(t *testing.T) {
-	type Foo struct {
-		A int
-		B int
-		C int
-	}
+		fields := m.TypeMap(reflect.TypeOf(post))
+		assert.Equal(t, 3, len(fields.Index))
 
-	f := Foo{1, 2, 3}
-	m := NewMapperFunc("db", strings.ToLower)
+		assert.Equal(t, "Hiyo", m.FieldByName(pv, "asset.title").Interface().(string))
+		assert.Equal(t, "Joe", m.FieldByName(pv, "author").Interface().(string))
+	})
 
-	fm := m.FieldMap(reflect.ValueOf(f))
+	t.Run("PointerFieldsWithNames", func(t *testing.T) {
+		m := NewMapperTagFunc("db", strings.ToLower, nil)
 
-	if len(fm) != 3 {
-		t.Errorf("Expecting %d keys, got %d", 3, len(fm))
-	}
-	if fm["a"].Interface().(int) != 1 {
-		t.Errorf("Expecting %d, got %d", 1, ival(fm["a"]))
-	}
-	if fm["b"].Interface().(int) != 2 {
-		t.Errorf("Expecting %d, got %d", 2, ival(fm["b"]))
-	}
-	if fm["c"].Interface().(int) != 3 {
-		t.Errorf("Expecting %d, got %d", 3, ival(fm["c"]))
-	}
-}
+		type User struct {
+			Name string
+		}
 
-func TestTagNameMapping(t *testing.T) {
-	type Strategy struct {
-		StrategyID   string `protobuf:"bytes,1,opt,name=strategy_id" json:"strategy_id,omitempty"`
-		StrategyName string
-	}
+		type Asset struct {
+			Title string
 
-	m := NewMapperTagFunc("json", strings.ToUpper, func(value string) string {
-		if strings.Contains(value, ",") {
-			return strings.Split(value, ",")[0]
+			Owner *User `db:"owner"`
 		}
-		return value
-	})
-	strategy := Strategy{"1", "Alpah"}
-	mapping := m.TypeMap(reflect.TypeOf(strategy))
 
-	for _, key := range []string{"strategy_id", "STRATEGYNAME"} {
-		if fi := mapping.GetByPath(key); fi == nil {
-			t.Errorf("Expecting to find key %s in mapping but did not.", key)
+		type Post struct {
+			Author string
+
+			Asset1 *Asset `db:"asset1"`
+			Asset2 *Asset `db:"asset2"`
 		}
-	}
-}
 
-func TestMapping(t *testing.T) {
-	type Person struct {
-		ID           int
-		Name         string
-		WearsGlasses bool `db:"wears_glasses"`
-	}
+		post := &Post{
+			Author: "Joe",
+			Asset1: &Asset{
+				Title: "Hiyo",
+				Owner: &User{"Username"},
+			},
+		} // Asset2 is nil
+
+		pv := reflect.ValueOf(post)
 
-	m := NewMapperFunc("db", strings.ToLower)
-	p := Person{1, "Jason", true}
-	mapping := m.TypeMap(reflect.TypeOf(p))
+		fields := m.TypeMap(reflect.TypeOf(post))
+		assert.Equal(t, 9, len(fields.Index))
 
-	for _, key := range []string{"id", "name", "wears_glasses"} {
-		if fi := mapping.GetByPath(key); fi == nil {
-			t.Errorf("Expecting to find key %s in mapping but did not.", key)
+		assert.Equal(t, "Hiyo", m.FieldByName(pv, "asset1.title").Interface().(string))
+		assert.Equal(t, "Username", m.FieldByName(pv, "asset1.owner.name").Interface().(string))
+		assert.Equal(t, post.Asset2.Title, m.FieldByName(pv, "asset2.title").Interface().(string))
+		assert.Equal(t, post.Asset2.Owner.Name, m.FieldByName(pv, "asset2.owner.name").Interface().(string))
+		assert.Equal(t, post.Author, m.FieldByName(pv, "author").Interface().(string))
+	})
+
+	t.Run("NameMapping", func(t *testing.T) {
+		type Strategy struct {
+			StrategyID   string `protobuf:"bytes,1,opt,name=strategy_id" json:"strategy_id,omitempty"`
+			StrategyName string
 		}
-	}
 
-	type SportsPerson struct {
-		Weight int
-		Age    int
-		Person
-	}
-	s := SportsPerson{Weight: 100, Age: 30, Person: p}
-	mapping = m.TypeMap(reflect.TypeOf(s))
-	for _, key := range []string{"id", "name", "wears_glasses", "weight", "age"} {
-		if fi := mapping.GetByPath(key); fi == nil {
-			t.Errorf("Expecting to find key %s in mapping but did not.", key)
+		mapperTagFunc := NewMapperTagFunc("json", strings.ToUpper, func(value string) string {
+			if strings.Contains(value, ",") {
+				return strings.Split(value, ",")[0]
+			}
+			return value
+		})
+
+		strategy := Strategy{"1", "Alpha"}
+		m := mapperTagFunc.TypeMap(reflect.TypeOf(strategy))
+
+		assert.NotNil(t, m.GetByPath("strategy_id"))  // explicitly tagged
+		assert.NotNil(t, m.GetByPath("STRATEGYNAME")) // mapped by name
+		assert.Nil(t, m.GetByPath("strategyname"))    // not mapped by tag
+		assert.Nil(t, m.GetByPath("STRATEGYID"))      // not mapped by tag
+	})
+
+	t.Run("MapperFuncWithTags", func(t *testing.T) {
+		type Person struct {
+			ID           int
+			Name         string
+			WearsGlasses bool `db:"wears_glasses"`
 		}
-	}
 
-	type RugbyPlayer struct {
-		Position   int
-		IsIntense  bool `db:"is_intense"`
-		IsAllBlack bool `db:"-"`
-		SportsPerson
-	}
-	r := RugbyPlayer{12, true, false, s}
-	mapping = m.TypeMap(reflect.TypeOf(r))
-	for _, key := range []string{"id", "name", "wears_glasses", "weight", "age", "position", "is_intense"} {
-		if fi := mapping.GetByPath(key); fi == nil {
-			t.Errorf("Expecting to find key %s in mapping but did not.", key)
+		m := NewMapperFunc("db", strings.ToLower)
+		p := Person{1, "Jason", true}
+		mapping := m.TypeMap(reflect.TypeOf(p))
+
+		assert.NotNil(t, mapping.GetByPath("id"))
+		assert.NotNil(t, mapping.GetByPath("name"))
+		assert.NotNil(t, mapping.GetByPath("wears_glasses"))
+
+		type SportsPerson struct {
+			Weight int
+			Age    int
+			Person
 		}
-	}
+		s := SportsPerson{Weight: 100, Age: 30, Person: p}
+		mapping = m.TypeMap(reflect.TypeOf(s))
+
+		assert.NotNil(t, mapping.GetByPath("id"))
+		assert.NotNil(t, mapping.GetByPath("name"))
+		assert.NotNil(t, mapping.GetByPath("wears_glasses"))
+		assert.NotNil(t, mapping.GetByPath("weight"))
+		assert.NotNil(t, mapping.GetByPath("age"))
+
+		type RugbyPlayer struct {
+			Position   int
+			IsIntense  bool `db:"is_intense"`
+			IsAllBlack bool `db:"-"`
+			SportsPerson
+		}
+		r := RugbyPlayer{12, true, false, s}
+		mapping = m.TypeMap(reflect.TypeOf(r))
 
-	if fi := mapping.GetByPath("isallblack"); fi != nil {
-		t.Errorf("Expecting to ignore `IsAllBlack` field")
-	}
-}
+		assert.NotNil(t, mapping.GetByPath("id"))
+		assert.NotNil(t, mapping.GetByPath("name"))
+		assert.NotNil(t, mapping.GetByPath("wears_glasses"))
+		assert.NotNil(t, mapping.GetByPath("weight"))
+		assert.NotNil(t, mapping.GetByPath("age"))
+		assert.NotNil(t, mapping.GetByPath("position"))
 
-type E1 struct {
-	A int
-}
-type E2 struct {
-	E1
-	B int
-}
-type E3 struct {
-	E2
-	C int
-}
-type E4 struct {
-	E3
-	D int
+		assert.Nil(t, mapping.GetByPath("isallblack"))
+	})
 }
 
 func BenchmarkFieldNameL1(b *testing.B) {
diff --git a/internal/sqladapter/sqladapter_test.go b/internal/sqladapter/sqladapter_test.go
index a95275a..a0c797e 100644
--- a/internal/sqladapter/sqladapter_test.go
+++ b/internal/sqladapter/sqladapter_test.go
@@ -1,6 +1,7 @@
 package sqladapter
 
 import (
+	"fmt"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -39,7 +40,9 @@ func TestReplaceWithDollarSign(t *testing.T) {
 		},
 	}
 
-	for _, test := range tests {
-		assert.Equal(t, []byte(test.out), ReplaceWithDollarSign([]byte(test.in)))
+	for i, test := range tests {
+		t.Run(fmt.Sprintf("Case_%03d", i), func(t *testing.T) {
+			assert.Equal(t, []byte(test.out), ReplaceWithDollarSign([]byte(test.in)))
+		})
 	}
 }
diff --git a/internal/sqlbuilder/builder_test.go b/internal/sqlbuilder/builder_test.go
index 4842b38..c72449d 100644
--- a/internal/sqlbuilder/builder_test.go
+++ b/internal/sqlbuilder/builder_test.go
@@ -7,15 +7,202 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
-	db "github.com/upper/db/v4"
+	"github.com/upper/db/v4"
+	"github.com/upper/db/v4/internal/cache"
+	"github.com/upper/db/v4/internal/sqladapter/exql"
 )
 
 var (
 	reInvisibleChars = regexp.MustCompile(`[\s\r\n\t]+`)
 )
 
-func TestSelect(t *testing.T) {
+const (
+	defaultColumnSeparator     = `.`
+	defaultIdentifierSeparator = `, `
+	defaultIdentifierQuote     = `"{{.Value}}"`
+	defaultValueSeparator      = `, `
+	defaultValueQuote          = `'{{.}}'`
+	defaultAndKeyword          = `AND`
+	defaultOrKeyword           = `OR`
+	defaultDescKeyword         = `DESC`
+	defaultAscKeyword          = `ASC`
+	defaultAssignmentOperator  = `=`
+	defaultClauseGroup         = `({{.}})`
+	defaultClauseOperator      = ` {{.}} `
+	defaultColumnValue         = `{{.Column}} {{.Operator}} {{.Value}}`
+	defaultTableAliasLayout    = `{{.Name}}{{if .Alias}} AS {{.Alias}}{{end}}`
+	defaultColumnAliasLayout   = `{{.Name}}{{if .Alias}} AS {{.Alias}}{{end}}`
+	defaultSortByColumnLayout  = `{{.Column}} {{.Order}}`
+
+	defaultOrderByLayout = `
+    {{if .SortColumns}}
+      ORDER BY {{.SortColumns}}
+    {{end}}
+  `
+
+	defaultWhereLayout = `
+    {{if .Conds}}
+      WHERE {{.Conds}}
+    {{end}}
+  `
+
+	defaultUsingLayout = `
+    {{if .Columns}}
+      USING ({{.Columns}})
+    {{end}}
+  `
+
+	defaultJoinLayout = `
+    {{if .Table}}
+      {{ if .On }}
+        {{.Type}} JOIN {{.Table}}
+        {{.On}}
+      {{ else if .Using }}
+        {{.Type}} JOIN {{.Table}}
+        {{.Using}}
+      {{ else if .Type | eq "CROSS" }}
+        {{.Type}} JOIN {{.Table}}
+      {{else}}
+        NATURAL {{.Type}} JOIN {{.Table}}
+      {{end}}
+    {{end}}
+  `
+
+	defaultOnLayout = `
+    {{if .Conds}}
+      ON {{.Conds}}
+    {{end}}
+  `
+
+	defaultSelectLayout = `
+    SELECT
+      {{if .Distinct}}
+        DISTINCT
+      {{end}}
+
+      {{if defined .Columns}}
+        {{.Columns | compile}}
+      {{else}}
+        *
+      {{end}}
+
+      {{if defined .Table}}
+        FROM {{.Table | compile}}
+      {{end}}
+
+      {{.Joins | compile}}
+
+      {{.Where | compile}}
+
+      {{if defined .GroupBy}}
+        {{.GroupBy | compile}}
+      {{end}}
+
+      {{.OrderBy | compile}}
+
+      {{if .Limit}}
+        LIMIT {{.Limit}}
+      {{end}}
+
+      {{if .Offset}}
+        OFFSET {{.Offset}}
+      {{end}}
+  `
+	defaultDeleteLayout = `
+    DELETE
+      FROM {{.Table | compile}}
+      {{.Where | compile}}
+  `
+	defaultUpdateLayout = `
+    UPDATE
+      {{.Table | compile}}
+    SET {{.ColumnValues | compile}}
+      {{.Where | compile}}
+  `
+
+	defaultCountLayout = `
+    SELECT
+      COUNT(1) AS _t
+    FROM {{.Table | compile}}
+      {{.Where | compile}}
+
+      {{if .Limit}}
+        LIMIT {{.Limit}}
+      {{end}}
+
+      {{if .Offset}}
+        OFFSET {{.Offset}}
+      {{end}}
+  `
+
+	defaultInsertLayout = `
+    INSERT INTO {{.Table | compile}}
+      {{if defined .Columns }}({{.Columns | compile}}){{end}}
+    VALUES
+    {{if defined .Values}}
+      {{.Values | compile}}
+    {{else}}
+      (default)
+    {{end}}
+    {{if defined .Returning}}
+      RETURNING {{.Returning | compile}}
+    {{end}}
+  `
+
+	defaultTruncateLayout = `
+    TRUNCATE TABLE {{.Table | compile}}
+  `
+
+	defaultDropDatabaseLayout = `
+    DROP DATABASE {{.Database | compile}}
+  `
+
+	defaultDropTableLayout = `
+    DROP TABLE {{.Table | compile}}
+  `
+
+	defaultGroupByLayout = `
+    {{if .GroupColumns}}
+      GROUP BY {{.GroupColumns}}
+    {{end}}
+  `
+)
 
+var testTemplate = exql.Template{
+	ColumnSeparator:     defaultColumnSeparator,
+	IdentifierSeparator: defaultIdentifierSeparator,
+	IdentifierQuote:     defaultIdentifierQuote,
+	ValueSeparator:      defaultValueSeparator,
+	ValueQuote:          defaultValueQuote,
+	AndKeyword:          defaultAndKeyword,
+	OrKeyword:           defaultOrKeyword,
+	DescKeyword:         defaultDescKeyword,
+	AscKeyword:          defaultAscKeyword,
+	AssignmentOperator:  defaultAssignmentOperator,
+	ClauseGroup:         defaultClauseGroup,
+	ClauseOperator:      defaultClauseOperator,
+	ColumnValue:         defaultColumnValue,
+	TableAliasLayout:    defaultTableAliasLayout,
+	ColumnAliasLayout:   defaultColumnAliasLayout,
+	SortByColumnLayout:  defaultSortByColumnLayout,
+	WhereLayout:         defaultWhereLayout,
+	OnLayout:            defaultOnLayout,
+	UsingLayout:         defaultUsingLayout,
+	JoinLayout:          defaultJoinLayout,
+	OrderByLayout:       defaultOrderByLayout,
+	InsertLayout:        defaultInsertLayout,
+	SelectLayout:        defaultSelectLayout,
+	UpdateLayout:        defaultUpdateLayout,
+	DeleteLayout:        defaultDeleteLayout,
+	TruncateLayout:      defaultTruncateLayout,
+	DropDatabaseLayout:  defaultDropDatabaseLayout,
+	DropTableLayout:     defaultDropTableLayout,
+	CountLayout:         defaultCountLayout,
+	GroupByLayout:       defaultGroupByLayout,
+	Cache:               cache.NewCache(),
+}
+
+func TestSelect(t *testing.T) {
 	b := &sqlBuilder{t: newTemplateWithUtils(&testTemplate)}
 	assert := assert.New(t)
 
diff --git a/internal/sqlbuilder/placeholder_test.go b/internal/sqlbuilder/placeholder_test.go
index 93317f5..00aec9c 100644
--- a/internal/sqlbuilder/placeholder_test.go
+++ b/internal/sqlbuilder/placeholder_test.go
@@ -1,6 +1,7 @@
 package sqlbuilder
 
 import (
+	"fmt"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -65,8 +66,10 @@ func TestPrepareForDisplay(t *testing.T) {
 			Out: "$1$2$3",
 		},
 	}
-	for _, sample := range samples {
-		assert.Equal(t, sample.Out, prepareQueryForDisplay(sample.In))
+	for i, sample := range samples {
+		t.Run(fmt.Sprintf("sample %d", i), func(t *testing.T) {
+			assert.Equal(t, sample.Out, prepareQueryForDisplay(sample.In))
+		})
 	}
 }
 
diff --git a/internal/sqlbuilder/template_test.go b/internal/sqlbuilder/template_test.go
deleted file mode 100644
index 8d617e0..0000000
--- a/internal/sqlbuilder/template_test.go
+++ /dev/null
@@ -1,192 +0,0 @@
-package sqlbuilder
-
-import (
-	"github.com/upper/db/v4/internal/cache"
-	"github.com/upper/db/v4/internal/sqladapter/exql"
-)
-
-const (
-	defaultColumnSeparator     = `.`
-	defaultIdentifierSeparator = `, `
-	defaultIdentifierQuote     = `"{{.Value}}"`
-	defaultValueSeparator      = `, `
-	defaultValueQuote          = `'{{.}}'`
-	defaultAndKeyword          = `AND`
-	defaultOrKeyword           = `OR`
-	defaultDescKeyword         = `DESC`
-	defaultAscKeyword          = `ASC`
-	defaultAssignmentOperator  = `=`
-	defaultClauseGroup         = `({{.}})`
-	defaultClauseOperator      = ` {{.}} `
-	defaultColumnValue         = `{{.Column}} {{.Operator}} {{.Value}}`
-	defaultTableAliasLayout    = `{{.Name}}{{if .Alias}} AS {{.Alias}}{{end}}`
-	defaultColumnAliasLayout   = `{{.Name}}{{if .Alias}} AS {{.Alias}}{{end}}`
-	defaultSortByColumnLayout  = `{{.Column}} {{.Order}}`
-
-	defaultOrderByLayout = `
-    {{if .SortColumns}}
-      ORDER BY {{.SortColumns}}
-    {{end}}
-  `
-
-	defaultWhereLayout = `
-    {{if .Conds}}
-      WHERE {{.Conds}}
-    {{end}}
-  `
-
-	defaultUsingLayout = `
-    {{if .Columns}}
-      USING ({{.Columns}})
-    {{end}}
-  `
-
-	defaultJoinLayout = `
-    {{if .Table}}
-      {{ if .On }}
-        {{.Type}} JOIN {{.Table}}
-        {{.On}}
-      {{ else if .Using }}
-        {{.Type}} JOIN {{.Table}}
-        {{.Using}}
-      {{ else if .Type | eq "CROSS" }}
-        {{.Type}} JOIN {{.Table}}
-      {{else}}
-        NATURAL {{.Type}} JOIN {{.Table}}
-      {{end}}
-    {{end}}
-  `
-
-	defaultOnLayout = `
-    {{if .Conds}}
-      ON {{.Conds}}
-    {{end}}
-  `
-
-	defaultSelectLayout = `
-    SELECT
-      {{if .Distinct}}
-        DISTINCT
-      {{end}}
-
-      {{if defined .Columns}}
-        {{.Columns | compile}}
-      {{else}}
-        *
-      {{end}}
-
-      {{if defined .Table}}
-        FROM {{.Table | compile}}
-      {{end}}
-
-      {{.Joins | compile}}
-
-      {{.Where | compile}}
-
-      {{if defined .GroupBy}}
-        {{.GroupBy | compile}}
-      {{end}}
-
-      {{.OrderBy | compile}}
-
-      {{if .Limit}}
-        LIMIT {{.Limit}}
-      {{end}}
-
-      {{if .Offset}}
-        OFFSET {{.Offset}}
-      {{end}}
-  `
-	defaultDeleteLayout = `
-    DELETE
-      FROM {{.Table | compile}}
-      {{.Where | compile}}
-  `
-	defaultUpdateLayout = `
-    UPDATE
-      {{.Table | compile}}
-    SET {{.ColumnValues | compile}}
-      {{.Where | compile}}
-  `
-
-	defaultCountLayout = `
-    SELECT
-      COUNT(1) AS _t
-    FROM {{.Table | compile}}
-      {{.Where | compile}}
-
-      {{if .Limit}}
-        LIMIT {{.Limit}}
-      {{end}}
-
-      {{if .Offset}}
-        OFFSET {{.Offset}}
-      {{end}}
-  `
-
-	defaultInsertLayout = `
-    INSERT INTO {{.Table | compile}}
-      {{if defined .Columns }}({{.Columns | compile}}){{end}}
-    VALUES
-    {{if defined .Values}}
-      {{.Values | compile}}
-    {{else}}
-      (default)
-    {{end}}
-    {{if defined .Returning}}
-      RETURNING {{.Returning | compile}}
-    {{end}}
-  `
-
-	defaultTruncateLayout = `
-    TRUNCATE TABLE {{.Table | compile}}
-  `
-
-	defaultDropDatabaseLayout = `
-    DROP DATABASE {{.Database | compile}}
-  `
-
-	defaultDropTableLayout = `
-    DROP TABLE {{.Table | compile}}
-  `
-
-	defaultGroupByLayout = `
-    {{if .GroupColumns}}
-      GROUP BY {{.GroupColumns}}
-    {{end}}
-  `
-)
-
-var testTemplate = exql.Template{
-	ColumnSeparator:     defaultColumnSeparator,
-	IdentifierSeparator: defaultIdentifierSeparator,
-	IdentifierQuote:     defaultIdentifierQuote,
-	ValueSeparator:      defaultValueSeparator,
-	ValueQuote:          defaultValueQuote,
-	AndKeyword:          defaultAndKeyword,
-	OrKeyword:           defaultOrKeyword,
-	DescKeyword:         defaultDescKeyword,
-	AscKeyword:          defaultAscKeyword,
-	AssignmentOperator:  defaultAssignmentOperator,
-	ClauseGroup:         defaultClauseGroup,
-	ClauseOperator:      defaultClauseOperator,
-	ColumnValue:         defaultColumnValue,
-	TableAliasLayout:    defaultTableAliasLayout,
-	ColumnAliasLayout:   defaultColumnAliasLayout,
-	SortByColumnLayout:  defaultSortByColumnLayout,
-	WhereLayout:         defaultWhereLayout,
-	OnLayout:            defaultOnLayout,
-	UsingLayout:         defaultUsingLayout,
-	JoinLayout:          defaultJoinLayout,
-	OrderByLayout:       defaultOrderByLayout,
-	InsertLayout:        defaultInsertLayout,
-	SelectLayout:        defaultSelectLayout,
-	UpdateLayout:        defaultUpdateLayout,
-	DeleteLayout:        defaultDeleteLayout,
-	TruncateLayout:      defaultTruncateLayout,
-	DropDatabaseLayout:  defaultDropDatabaseLayout,
-	DropTableLayout:     defaultDropTableLayout,
-	CountLayout:         defaultCountLayout,
-	GroupByLayout:       defaultGroupByLayout,
-	Cache:               cache.NewCache(),
-}
diff --git a/internal/testsuite/sql_suite.go b/internal/testsuite/sql_suite.go
index 6e68c87..844e0df 100644
--- a/internal/testsuite/sql_suite.go
+++ b/internal/testsuite/sql_suite.go
@@ -1886,13 +1886,16 @@ func (s *SQLTestSuite) TestCustomType() {
 }
 
 func (s *SQLTestSuite) Test_Issue565() {
-	s.Session().Collection("birthdays").Insert(&birthday{
+	_, err := s.Session().Collection("birthdays").Insert(&birthday{
 		Name: "Lucy",
 		Born: time.Now(),
 	})
+	s.NoError(err)
+
+	ctxKeyCarry := db.ContextKey("carry")
 
-	parentCtx := context.WithValue(s.Session().Context(), "carry", 1)
-	s.NotZero(parentCtx.Value("carry"))
+	parentCtx := context.WithValue(s.Session().Context(), ctxKeyCarry, 1)
+	s.NotZero(parentCtx.Value(ctxKeyCarry))
 
 	{
 		ctx, cancel := context.WithTimeout(parentCtx, time.Nanosecond)
@@ -1908,7 +1911,7 @@ func (s *SQLTestSuite) Test_Issue565() {
 		s.Error(err)
 		s.Zero(result.Name)
 
-		s.NotZero(ctx.Value("carry"))
+		s.NotZero(ctx.Value(ctxKeyCarry))
 	}
 
 	{
@@ -1923,7 +1926,7 @@ func (s *SQLTestSuite) Test_Issue565() {
 		s.Error(err)
 		s.Zero(result.Name)
 
-		s.NotZero(ctx.Value("carry"))
+		s.NotZero(ctx.Value(ctxKeyCarry))
 	}
 
 	{
@@ -1938,7 +1941,7 @@ func (s *SQLTestSuite) Test_Issue565() {
 		s.NoError(err)
 		s.NotZero(result.Name)
 
-		s.NotZero(ctx.Value("carry"))
+		s.NotZero(ctx.Value(ctxKeyCarry))
 	}
 }
 
diff --git a/logger_test.go b/logger_test.go
deleted file mode 100644
index e9b71fd..0000000
--- a/logger_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2012-present The upper.io/db authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-package db
-
-import (
-	"errors"
-	"testing"
-)
-
-func TestLogger(t *testing.T) {
-	err := errors.New("fake error")
-	LC().Error(err)
-}