mirror of
https://github.com/gofiber/storage.git
synced 2025-10-17 22:21:37 +08:00
chore: use functional options to define the hook for when to create a new store
This commit is contained in:
@@ -134,7 +134,7 @@ func Test_MYSQL_Non_UTF8(t *testing.T) {
|
||||
|
||||
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)
|
||||
s, err := tck.New[*Storage, *sql.DB](context.Background(), t, &MySQLStorageTCK{}, tck.PerTest())
|
||||
require.NoError(t, err)
|
||||
|
||||
suite.Run(t, &s)
|
||||
|
@@ -13,18 +13,59 @@ import (
|
||||
"github.com/gofiber/storage"
|
||||
)
|
||||
|
||||
// CreationHook defines when the store should be created.
|
||||
// suiteHook defines when the store should be created.
|
||||
// Please see [PerSuite] and [PerTest] for more details.
|
||||
type CreationHook int
|
||||
type suiteHook int
|
||||
|
||||
const (
|
||||
// PerTest defines that the store should be created per test.
|
||||
PerTest CreationHook = iota
|
||||
// perTest defines that the store should be created per test.
|
||||
perTest suiteHook = iota
|
||||
|
||||
// PerSuite defines that the store should be created per suite.
|
||||
PerSuite
|
||||
// perSuite defines that the store should be created per suite.
|
||||
perSuite
|
||||
)
|
||||
|
||||
// suiteUpdater is the interface that must be implemented by the test suite.
|
||||
// It defines how the [suiteOption]s update the suite.
|
||||
type suiteUpdater interface {
|
||||
updateHook(hook suiteHook) error
|
||||
}
|
||||
|
||||
// suiteOption is the interface that must be implemented by the [suiteOption]s.
|
||||
// It defines how the [suiteOption]s update the suite.
|
||||
type suiteOption interface {
|
||||
apply(s suiteUpdater) error
|
||||
}
|
||||
|
||||
// suiteUpdateOption is the default implementation of the [suiteOption] interface.
|
||||
// It is used to update the suite hook.
|
||||
type suiteUpdateOption struct {
|
||||
fn func(s suiteUpdater) error
|
||||
}
|
||||
|
||||
// apply is the method that is called by the [suiteOption]s to update the suite.
|
||||
func (o *suiteUpdateOption) apply(s suiteUpdater) error {
|
||||
return o.fn(s)
|
||||
}
|
||||
|
||||
// PerTest is a [suiteOption] that updates the suite hook to [perTest].
|
||||
func PerTest() suiteOption {
|
||||
return &suiteUpdateOption{
|
||||
fn: func(s suiteUpdater) error {
|
||||
return s.updateHook(perTest)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// PerSuite is a [suiteOption] that updates the suite hook to [perSuite].
|
||||
func PerSuite() suiteOption {
|
||||
return &suiteUpdateOption{
|
||||
fn: func(s suiteUpdater) error {
|
||||
return s.updateHook(perSuite)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TCKSuite is the interface that must be implemented by the test suite.
|
||||
// It defines how to create a new store with a container.
|
||||
// The generic parameters are the storage type and the driver type returned by the Conn method.
|
||||
@@ -33,20 +74,26 @@ type TCKSuite[T storage.Storage, D any] interface {
|
||||
}
|
||||
|
||||
// New creates a new [StorageTestSuite] with the given [TCKSuite].
|
||||
func New[T storage.Storage, D any](ctx context.Context, t *testing.T, tckSuite TCKSuite[T, D], creationHook CreationHook) (StorageTestSuite[T, D], error) {
|
||||
if creationHook != PerSuite && creationHook != PerTest {
|
||||
return StorageTestSuite[T, D]{}, fmt.Errorf("invalid creation hook: %d", creationHook)
|
||||
}
|
||||
|
||||
func New[T storage.Storage, D any](ctx context.Context, t *testing.T, tckSuite TCKSuite[T, D], opts ...suiteOption) (StorageTestSuite[T, D], error) {
|
||||
if tckSuite == nil {
|
||||
return StorageTestSuite[T, D]{}, fmt.Errorf("test suite is nil")
|
||||
}
|
||||
|
||||
return StorageTestSuite[T, D]{
|
||||
s := StorageTestSuite[T, D]{
|
||||
ctx: ctx,
|
||||
creationHook: creationHook,
|
||||
hook: perTest, // defaults to perTest
|
||||
createFn: tckSuite.NewStoreWithContainer(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
if err := opt.apply(&s); err != nil {
|
||||
return StorageTestSuite[T, D]{}, fmt.Errorf("apply option: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
s.SetT(t)
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// StorageTestSuite is the test suite for the storage.
|
||||
@@ -56,7 +103,7 @@ type StorageTestSuite[T storage.Storage, D any] struct {
|
||||
suite.Suite
|
||||
stats *suite.SuiteInformation
|
||||
ctx context.Context
|
||||
creationHook CreationHook
|
||||
hook suiteHook
|
||||
createFn func(ctx context.Context, tb testing.TB) (T, testcontainers.Container, error)
|
||||
store storage.Storage
|
||||
closedStore bool
|
||||
@@ -64,6 +111,15 @@ type StorageTestSuite[T storage.Storage, D any] struct {
|
||||
ctr testcontainers.Container
|
||||
}
|
||||
|
||||
func (s *StorageTestSuite[T, D]) updateHook(hook suiteHook) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.hook = hook
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanup is a helper function to cleanup the store and container.
|
||||
// To avoid double closing the store, it checks if the store is already closed.
|
||||
func (s *StorageTestSuite[T, D]) cleanup() error {
|
||||
@@ -101,7 +157,7 @@ func (s *StorageTestSuite[T, D]) HandleStats(_ string, stats *suite.SuiteInforma
|
||||
// SetupSuite is a hook that is called when the suite is setup.
|
||||
// It is used to create the store and container, only if the creation hook is [PerSuite].
|
||||
func (s *StorageTestSuite[T, D]) SetupSuite() {
|
||||
if s.creationHook == PerSuite {
|
||||
if s.hook == perSuite {
|
||||
t := s.T()
|
||||
|
||||
store, ctr, err := s.createFn(s.ctx, t)
|
||||
@@ -110,6 +166,7 @@ func (s *StorageTestSuite[T, D]) SetupSuite() {
|
||||
}
|
||||
s.store = store
|
||||
s.ctr = ctr
|
||||
s.closedStore = false
|
||||
|
||||
err = s.store.Reset()
|
||||
s.Require().NoError(err)
|
||||
@@ -119,7 +176,7 @@ func (s *StorageTestSuite[T, D]) SetupSuite() {
|
||||
// TearDownSuite is a hook that is called when the suite is torn down.
|
||||
// It is used to cleanup the store and container, only if the creation hook is [PerSuite].
|
||||
func (s *StorageTestSuite[T, D]) TearDownSuite() {
|
||||
if s.creationHook == PerSuite {
|
||||
if s.hook == perSuite {
|
||||
s.Require().NoError(s.cleanup())
|
||||
}
|
||||
}
|
||||
@@ -127,7 +184,7 @@ func (s *StorageTestSuite[T, D]) TearDownSuite() {
|
||||
// SetupTest is a hook that is called when the test is setup.
|
||||
// It is used to create the store and container, only if the creation hook is [PerTest].
|
||||
func (s *StorageTestSuite[T, D]) SetupTest() {
|
||||
if s.creationHook == PerTest {
|
||||
if s.hook == perTest {
|
||||
t := s.T()
|
||||
|
||||
store, ctr, err := s.createFn(s.ctx, t)
|
||||
@@ -136,6 +193,7 @@ func (s *StorageTestSuite[T, D]) SetupTest() {
|
||||
}
|
||||
s.store = store
|
||||
s.ctr = ctr
|
||||
s.closedStore = false
|
||||
|
||||
err = s.store.Reset()
|
||||
s.Require().NoError(err)
|
||||
@@ -145,7 +203,7 @@ func (s *StorageTestSuite[T, D]) SetupTest() {
|
||||
// TearDownTest is a hook that is called when the test is torn down.
|
||||
// It is used to cleanup the store and container, only if the creation hook is [PerTest].
|
||||
func (s *StorageTestSuite[T, D]) TearDownTest() {
|
||||
if s.creationHook == PerTest {
|
||||
if s.hook == perTest {
|
||||
s.Require().NoError(s.cleanup())
|
||||
}
|
||||
}
|
||||
@@ -212,7 +270,7 @@ func (s *StorageTestSuite[T, D]) TestGetExpired() {
|
||||
s.Eventually(func() bool {
|
||||
s.requireKeyNotExists("temp_key")
|
||||
return true
|
||||
}, 2*time.Second, 100*time.Millisecond, "Key should expire")
|
||||
}, 2*time.Second, 1*time.Second, "Key should expire")
|
||||
}
|
||||
|
||||
func (s *StorageTestSuite[T, D]) TestDelete() {
|
||||
|
Reference in New Issue
Block a user