mirror of
https://github.com/gofiber/storage.git
synced 2025-09-28 21:32:23 +08:00
205 lines
4.8 KiB
Go
205 lines
4.8 KiB
Go
package mysql
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gofiber/storage/testhelpers/tck"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/stretchr/testify/suite"
|
|
"github.com/testcontainers/testcontainers-go"
|
|
"github.com/testcontainers/testcontainers-go/modules/mysql"
|
|
"github.com/testcontainers/testcontainers-go/wait"
|
|
)
|
|
|
|
const (
|
|
// mysqlImage is the default image used for running MySQL in tests.
|
|
mysqlImage = "docker.io/mysql:9"
|
|
mysqlImageEnvVar string = "TEST_MYSQL_IMAGE"
|
|
mysqlUser string = "username"
|
|
mysqlPass string = "password"
|
|
mysqlDatabase string = "fiber"
|
|
)
|
|
|
|
// MySQLStorageTCK is the test suite for the MySQL storage.
|
|
type MySQLStorageTCK struct{}
|
|
|
|
// NewStore is a function that returns a new MySQL storage.
|
|
// It implements the [tck.TCKSuite] interface, allowing the TCK to create a new MySQL storage
|
|
// from the container created by the TCK.
|
|
func (s *MySQLStorageTCK) NewStore() func(ctx context.Context, tb testing.TB, ctr *mysql.MySQLContainer) (*Storage, error) {
|
|
return func(ctx context.Context, tb testing.TB, ctr *mysql.MySQLContainer) (*Storage, error) {
|
|
conn, err := ctr.ConnectionString(ctx)
|
|
require.NoError(tb, err)
|
|
|
|
store := New(Config{
|
|
ConnectionURI: conn,
|
|
Reset: true,
|
|
})
|
|
|
|
return store, nil
|
|
}
|
|
}
|
|
|
|
// NewContainer is a function that returns a new MySQL container.
|
|
// It implements the [tck.TCKSuite] interface, allowing the TCK to create a new MySQL container
|
|
// for the MySQL storage.
|
|
func (s *MySQLStorageTCK) NewContainer() func(ctx context.Context, tb testing.TB) (*mysql.MySQLContainer, error) {
|
|
return func(ctx context.Context, tb testing.TB) (*mysql.MySQLContainer, error) {
|
|
return mustStartMySQL(tb), nil
|
|
}
|
|
}
|
|
|
|
func newTestStore(t testing.TB) *Storage {
|
|
t.Helper()
|
|
|
|
ctx := context.Background()
|
|
|
|
suite := MySQLStorageTCK{}
|
|
|
|
ctr, err := suite.NewContainer()(ctx, t)
|
|
require.NoError(t, err)
|
|
|
|
store, err := suite.NewStore()(ctx, t, ctr)
|
|
require.NoError(t, err)
|
|
|
|
return store
|
|
}
|
|
|
|
func mustStartMySQL(t testing.TB) *mysql.MySQLContainer {
|
|
img := mysqlImage
|
|
if imgFromEnv := os.Getenv(mysqlImageEnvVar); imgFromEnv != "" {
|
|
img = imgFromEnv
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
c, err := mysql.Run(ctx, img,
|
|
mysql.WithPassword(mysqlPass),
|
|
mysql.WithUsername(mysqlUser),
|
|
mysql.WithDatabase(mysqlDatabase),
|
|
testcontainers.WithWaitStrategy(
|
|
wait.ForListeningPort("3306/tcp"),
|
|
wait.ForLog("port: 3306 MySQL Community Server"),
|
|
),
|
|
)
|
|
testcontainers.CleanupContainer(t, c)
|
|
require.NoError(t, err)
|
|
|
|
return c
|
|
}
|
|
|
|
func Test_MYSQL_New(t *testing.T) {
|
|
c := mustStartMySQL(t)
|
|
|
|
dsn, err := c.ConnectionString(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
db, _ := sql.Open("mysql", dsn)
|
|
newConfigStore := New(Config{
|
|
Db: db,
|
|
Reset: true,
|
|
})
|
|
|
|
require.True(t, newConfigStore.db != nil)
|
|
defer newConfigStore.Close()
|
|
}
|
|
|
|
func Test_MYSQL_GC(t *testing.T) {
|
|
testVal := []byte("doe")
|
|
|
|
// This key should expire
|
|
testStore := newTestStore(t)
|
|
defer testStore.Close()
|
|
|
|
err := testStore.Set("john", testVal, time.Nanosecond)
|
|
require.NoError(t, err)
|
|
|
|
testStore.gc(time.Now())
|
|
row := testStore.db.QueryRow(testStore.sqlSelect, "john")
|
|
err = row.Scan(nil, nil)
|
|
require.Equal(t, sql.ErrNoRows, err)
|
|
|
|
// This key should not expire
|
|
err = testStore.Set("john", testVal, 0)
|
|
require.NoError(t, err)
|
|
|
|
testStore.gc(time.Now())
|
|
val, err := testStore.Get("john")
|
|
require.NoError(t, err)
|
|
require.Equal(t, testVal, val)
|
|
}
|
|
|
|
func Test_MYSQL_Non_UTF8(t *testing.T) {
|
|
val := []byte("0xF5")
|
|
|
|
testStore := newTestStore(t)
|
|
defer testStore.Close()
|
|
|
|
err := testStore.Set("0xF6", val, 0)
|
|
require.NoError(t, err)
|
|
|
|
result, err := testStore.Get("0xF6")
|
|
require.NoError(t, err)
|
|
require.Equal(t, val, result)
|
|
}
|
|
|
|
func TestMySQLStorageTCK(t *testing.T) {
|
|
// The TCK needs the concrete type of the storage and the driver type returned by the Conn method.
|
|
s, err := tck.New[*Storage, *sql.DB](context.Background(), t, &MySQLStorageTCK{}, tck.PerTest())
|
|
require.NoError(t, err)
|
|
|
|
suite.Run(t, s)
|
|
}
|
|
|
|
func Benchmark_MYSQL_Set(b *testing.B) {
|
|
testStore := newTestStore(b)
|
|
defer testStore.Close()
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
var err error
|
|
for i := 0; i < b.N; i++ {
|
|
err = testStore.Set("john", []byte("doe"), 0)
|
|
}
|
|
|
|
require.NoError(b, err)
|
|
}
|
|
|
|
func Benchmark_MYSQL_Get(b *testing.B) {
|
|
testStore := newTestStore(b)
|
|
defer testStore.Close()
|
|
|
|
err := testStore.Set("john", []byte("doe"), 0)
|
|
require.NoError(b, err)
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
_, err = testStore.Get("john")
|
|
}
|
|
|
|
require.NoError(b, err)
|
|
}
|
|
|
|
func Benchmark_MYSQL_SetAndDelete(b *testing.B) {
|
|
testStore := newTestStore(b)
|
|
defer testStore.Close()
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
var err error
|
|
for i := 0; i < b.N; i++ {
|
|
_ = testStore.Set("john", []byte("doe"), 0)
|
|
err = testStore.Delete("john")
|
|
}
|
|
|
|
require.NoError(b, err)
|
|
}
|