Skip to content

Commit 7c6c81b

Browse files
authored
Merge pull request #1885 from mdelapenya/storage-tck
2 parents f7ca2c4 + bbd28b9 commit 7c6c81b

File tree

8 files changed

+1205
-231
lines changed

8 files changed

+1205
-231
lines changed

mysql/go.mod

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@ module github.com/gofiber/storage/mysql/v2
22

33
go 1.23.0
44

5+
replace (
6+
github.com/gofiber/storage => ../
7+
github.com/gofiber/storage/testhelpers/tck => ../testhelpers/tck
8+
)
9+
510
require (
611
github.com/go-sql-driver/mysql v1.9.3
12+
github.com/gofiber/storage/testhelpers/tck v1.0.0
713
github.com/stretchr/testify v1.10.0
814
github.com/testcontainers/testcontainers-go v0.38.0
915
github.com/testcontainers/testcontainers-go/modules/mysql v0.38.0
@@ -30,6 +36,7 @@ require (
3036
github.com/go-logr/logr v1.4.2 // indirect
3137
github.com/go-logr/stdr v1.2.2 // indirect
3238
github.com/go-ole/go-ole v1.2.6 // indirect
39+
github.com/gofiber/storage v1.3.3 // indirect
3340
github.com/gogo/protobuf v1.3.2 // indirect
3441
github.com/google/uuid v1.6.0 // indirect
3542
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect

mysql/mysql_test.go

Lines changed: 42 additions & 231 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import (
77
"testing"
88
"time"
99

10+
"github.com/gofiber/storage/testhelpers/tck"
1011
"github.com/stretchr/testify/require"
12+
"github.com/stretchr/testify/suite"
1113
"github.com/testcontainers/testcontainers-go"
1214
"github.com/testcontainers/testcontainers-go/modules/mysql"
1315
"github.com/testcontainers/testcontainers-go/wait"
@@ -22,20 +24,49 @@ const (
2224
mysqlDatabase string = "fiber"
2325
)
2426

27+
// MySQLStorageTCK is the test suite for the MySQL storage.
28+
type MySQLStorageTCK struct{}
29+
30+
// NewStore is a function that returns a new MySQL storage.
31+
// It implements the [tck.TCKSuite] interface, allowing the TCK to create a new MySQL storage
32+
// from the container created by the TCK.
33+
func (s *MySQLStorageTCK) NewStore() func(ctx context.Context, tb testing.TB, ctr *mysql.MySQLContainer) (*Storage, error) {
34+
return func(ctx context.Context, tb testing.TB, ctr *mysql.MySQLContainer) (*Storage, error) {
35+
conn, err := ctr.ConnectionString(ctx)
36+
require.NoError(tb, err)
37+
38+
store := New(Config{
39+
ConnectionURI: conn,
40+
Reset: true,
41+
})
42+
43+
return store, nil
44+
}
45+
}
46+
47+
// NewContainer is a function that returns a new MySQL container.
48+
// It implements the [tck.TCKSuite] interface, allowing the TCK to create a new MySQL container
49+
// for the MySQL storage.
50+
func (s *MySQLStorageTCK) NewContainer() func(ctx context.Context, tb testing.TB) (*mysql.MySQLContainer, error) {
51+
return func(ctx context.Context, tb testing.TB) (*mysql.MySQLContainer, error) {
52+
return mustStartMySQL(tb), nil
53+
}
54+
}
55+
2556
func newTestStore(t testing.TB) *Storage {
2657
t.Helper()
2758

2859
ctx := context.Background()
2960

30-
c := mustStartMySQL(t)
61+
suite := MySQLStorageTCK{}
3162

32-
conn, err := c.ConnectionString(ctx)
63+
ctr, err := suite.NewContainer()(ctx, t)
3364
require.NoError(t, err)
3465

35-
return New(Config{
36-
ConnectionURI: conn,
37-
Reset: true,
38-
})
66+
store, err := suite.NewStore()(ctx, t, ctr)
67+
require.NoError(t, err)
68+
69+
return store
3970
}
4071

4172
func mustStartMySQL(t testing.TB) *mysql.MySQLContainer {
@@ -77,222 +108,6 @@ func Test_MYSQL_New(t *testing.T) {
77108
defer newConfigStore.Close()
78109
}
79110

80-
func Test_MYSQL_Set(t *testing.T) {
81-
var (
82-
key = "john"
83-
val = []byte("doe")
84-
)
85-
86-
testStore := newTestStore(t)
87-
defer testStore.Close()
88-
89-
err := testStore.Set(key, val, 0)
90-
require.NoError(t, err)
91-
}
92-
93-
func Test_MYSQL_SetWithContext(t *testing.T) {
94-
var (
95-
key = "john"
96-
val = []byte("doe")
97-
)
98-
99-
ctx, cancel := context.WithCancel(context.Background())
100-
cancel()
101-
102-
testStore := newTestStore(t)
103-
defer testStore.Close()
104-
105-
err := testStore.SetWithContext(ctx, key, val, 0)
106-
require.ErrorIs(t, err, context.Canceled)
107-
}
108-
109-
func Test_MYSQL_Set_Override(t *testing.T) {
110-
var (
111-
key = "john"
112-
val = []byte("doe")
113-
)
114-
115-
testStore := newTestStore(t)
116-
defer testStore.Close()
117-
118-
err := testStore.Set(key, val, 0)
119-
require.NoError(t, err)
120-
121-
err = testStore.Set(key, val, 0)
122-
require.NoError(t, err)
123-
}
124-
125-
func Test_MYSQL_Get(t *testing.T) {
126-
var (
127-
key = "john"
128-
val = []byte("doe")
129-
)
130-
131-
testStore := newTestStore(t)
132-
defer testStore.Close()
133-
134-
err := testStore.Set(key, val, 0)
135-
require.NoError(t, err)
136-
137-
result, err := testStore.Get(key)
138-
require.NoError(t, err)
139-
require.Equal(t, val, result)
140-
}
141-
142-
func Test_MYSQL_GetWithContext(t *testing.T) {
143-
var (
144-
key = "john"
145-
val = []byte("doe")
146-
)
147-
148-
testStore := newTestStore(t)
149-
defer testStore.Close()
150-
151-
err := testStore.Set(key, val, 0)
152-
require.NoError(t, err)
153-
154-
ctx, cancel := context.WithCancel(context.Background())
155-
cancel()
156-
157-
result, err := testStore.GetWithContext(ctx, key)
158-
require.ErrorIs(t, err, context.Canceled)
159-
require.Zero(t, len(result))
160-
}
161-
162-
func Test_MYSQL_Set_Expiration(t *testing.T) {
163-
var (
164-
key = "john"
165-
val = []byte("doe")
166-
exp = 1 * time.Second
167-
)
168-
169-
testStore := newTestStore(t)
170-
defer testStore.Close()
171-
172-
err := testStore.Set(key, val, exp)
173-
require.NoError(t, err)
174-
175-
time.Sleep(1100 * time.Millisecond)
176-
177-
result, err := testStore.Get(key)
178-
require.NoError(t, err)
179-
require.Zero(t, len(result), "Key should have expired")
180-
}
181-
182-
func Test_MYSQL_Get_Expired(t *testing.T) {
183-
key := "john"
184-
185-
testStore := newTestStore(t)
186-
defer testStore.Close()
187-
188-
result, err := testStore.Get(key)
189-
require.NoError(t, err)
190-
require.Zero(t, len(result))
191-
}
192-
193-
func Test_MYSQL_Get_NotExist(t *testing.T) {
194-
testStore := newTestStore(t)
195-
defer testStore.Close()
196-
197-
result, err := testStore.Get("notexist")
198-
require.NoError(t, err)
199-
require.Zero(t, len(result))
200-
}
201-
202-
func Test_MYSQL_Delete(t *testing.T) {
203-
var (
204-
key = "john"
205-
val = []byte("doe")
206-
)
207-
208-
testStore := newTestStore(t)
209-
defer testStore.Close()
210-
211-
err := testStore.Set(key, val, 0)
212-
require.NoError(t, err)
213-
214-
err = testStore.Delete(key)
215-
require.NoError(t, err)
216-
217-
result, err := testStore.Get(key)
218-
require.NoError(t, err)
219-
require.Zero(t, len(result))
220-
}
221-
222-
func Test_MYSQL_DeleteWithContext(t *testing.T) {
223-
var (
224-
key = "john"
225-
val = []byte("doe")
226-
)
227-
228-
testStore := newTestStore(t)
229-
defer testStore.Close()
230-
231-
err := testStore.Set(key, val, 0)
232-
require.NoError(t, err)
233-
234-
ctx, cancel := context.WithCancel(context.Background())
235-
cancel()
236-
237-
err = testStore.DeleteWithContext(ctx, key)
238-
require.ErrorIs(t, err, context.Canceled)
239-
240-
result, err := testStore.Get(key)
241-
require.NoError(t, err)
242-
require.Equal(t, val, result)
243-
}
244-
245-
func Test_MYSQL_Reset(t *testing.T) {
246-
val := []byte("doe")
247-
248-
testStore := newTestStore(t)
249-
defer testStore.Close()
250-
251-
err := testStore.Set("john1", val, 0)
252-
require.NoError(t, err)
253-
254-
err = testStore.Set("john2", val, 0)
255-
require.NoError(t, err)
256-
257-
err = testStore.Reset()
258-
require.NoError(t, err)
259-
260-
result, err := testStore.Get("john1")
261-
require.NoError(t, err)
262-
require.Zero(t, len(result))
263-
264-
result, err = testStore.Get("john2")
265-
require.NoError(t, err)
266-
require.Zero(t, len(result))
267-
}
268-
269-
func Test_MYSQL_ResetWithContext(t *testing.T) {
270-
val := []byte("doe")
271-
272-
testStore := newTestStore(t)
273-
defer testStore.Close()
274-
275-
err := testStore.Set("john1", val, 0)
276-
require.NoError(t, err)
277-
278-
err = testStore.Set("john2", val, 0)
279-
require.NoError(t, err)
280-
281-
ctx, cancel := context.WithCancel(context.Background())
282-
cancel()
283-
284-
err = testStore.ResetWithContext(ctx)
285-
require.ErrorIs(t, err, context.Canceled)
286-
287-
result, err := testStore.Get("john1")
288-
require.NoError(t, err)
289-
require.Equal(t, val, result)
290-
291-
result, err = testStore.Get("john2")
292-
require.NoError(t, err)
293-
require.Equal(t, val, result)
294-
}
295-
296111
func Test_MYSQL_GC(t *testing.T) {
297112
testVal := []byte("doe")
298113

@@ -332,16 +147,12 @@ func Test_MYSQL_Non_UTF8(t *testing.T) {
332147
require.Equal(t, val, result)
333148
}
334149

335-
func Test_MYSQL_Close(t *testing.T) {
336-
testStore := newTestStore(t)
337-
require.NoError(t, testStore.Close())
338-
}
339-
340-
func Test_MYSQL_Conn(t *testing.T) {
341-
testStore := newTestStore(t)
342-
defer testStore.Close()
150+
func TestMySQLStorageTCK(t *testing.T) {
151+
// The TCK needs the concrete type of the storage and the driver type returned by the Conn method.
152+
s, err := tck.New[*Storage, *sql.DB](context.Background(), t, &MySQLStorageTCK{}, tck.PerTest())
153+
require.NoError(t, err)
343154

344-
require.True(t, testStore.Conn() != nil)
155+
suite.Run(t, s)
345156
}
346157

347158
func Benchmark_MYSQL_Set(b *testing.B) {

storage.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ import (
55
"time"
66
)
77

8+
type StorageWithConn[T any] interface {
9+
// Conn returns a connection to the storage.
10+
// Implementations should return a connection to the storage,
11+
// using the proper driver for the storage.
12+
Conn() T
13+
}
14+
815
// Storage interface for communicating with different database/key-value
916
// providers. Visit https://github.com/gofiber/storage for more info.
1017
type Storage interface {

0 commit comments

Comments
 (0)