Skip to content

Commit ece1f25

Browse files
mikeeeJoshVanL
andauthored
fix(tests): fix flaky mysql cert tests (#4547)
Signed-off-by: Mike Nguyen <hey@mike.ee> Co-authored-by: Josh van Leeuwen <me@joshvanl.dev>
1 parent 2ee79a1 commit ece1f25

2 files changed

Lines changed: 68 additions & 43 deletions

File tree

tests/certification/bindings/mysql/docker-compose.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,14 @@ services:
1111
MYSQL_PASSWORD: example
1212
MYSQL_DATABASE: dapr_test
1313
MYSQL_ROOT_PASSWORD: root
14+
healthcheck:
15+
test:
16+
- CMD-SHELL
17+
- >-
18+
mysql --protocol=TCP --host=127.0.0.1
19+
--user="$${MYSQL_USER}" --password="$${MYSQL_PASSWORD}"
20+
--database="$${MYSQL_DATABASE}" --execute="SELECT 1" >/dev/null 2>&1
21+
interval: 1s
22+
timeout: 5s
23+
retries: 60
24+
start_period: 5s

tests/certification/bindings/mysql/mysql_test.go

Lines changed: 57 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"github.com/dapr/components-contrib/tests/certification/flow"
3838
"github.com/dapr/components-contrib/tests/certification/flow/dockercompose"
3939
"github.com/dapr/components-contrib/tests/certification/flow/network"
40+
"github.com/dapr/components-contrib/tests/certification/flow/retry"
4041
"github.com/dapr/components-contrib/tests/certification/flow/sidecar"
4142
)
4243

@@ -49,6 +50,46 @@ const (
4950
// MySQL doesn't accept RFC3339 formatted time, rejects trailing 'Z' for UTC indicator.
5051
const mySQLDateTimeFormat = "2006-01-02 15:04:05"
5152

53+
func checkMySQLConnection(ctx flow.Context) error {
54+
db, err := sql.Open("mysql", dockerConnectionString)
55+
if err != nil {
56+
return fmt.Errorf("failed to open MySQL connection: %w", err)
57+
}
58+
defer func() {
59+
_ = db.Close()
60+
}()
61+
62+
var result int
63+
err = db.QueryRowContext(ctx, "SELECT 1").Scan(&result)
64+
if err != nil {
65+
return fmt.Errorf("failed to query MySQL: %w", err)
66+
}
67+
if result != 1 {
68+
return fmt.Errorf("unexpected MySQL readiness result: %d", result)
69+
}
70+
71+
return nil
72+
}
73+
74+
func createTable(tableName string) flow.Runnable {
75+
return func(ctx flow.Context) error {
76+
db, err := sql.Open("mysql", dockerConnectionString)
77+
if err != nil {
78+
return fmt.Errorf("failed to open MySQL connection: %w", err)
79+
}
80+
defer func() {
81+
_ = db.Close()
82+
}()
83+
84+
_, err = db.ExecContext(ctx, "CREATE TABLE IF NOT EXISTS "+tableName+" (id INT, c1 TEXT, ts TIMESTAMP);")
85+
if err != nil {
86+
return fmt.Errorf("failed to create MySQL table %q: %w", tableName, err)
87+
}
88+
89+
return nil
90+
}
91+
}
92+
5293
func TestMysql(t *testing.T) {
5394
const tableName = "dapr_test_table"
5495

@@ -136,19 +177,10 @@ func TestMysql(t *testing.T) {
136177
return nil
137178
}
138179

139-
createTable := func(ctx flow.Context) error {
140-
db, err := sql.Open("mysql", dockerConnectionString)
141-
require.NoError(t, err)
142-
_, err = db.Exec("CREATE TABLE " + tableName + " (id INT, c1 TEXT, ts TIMESTAMP);")
143-
require.NoError(t, err)
144-
db.Close()
145-
return nil
146-
}
147-
148180
flow.New(t, "Run tests").
149181
Step(dockercompose.Run("db", dockerComposeYAML)).
150-
Step("wait for component to start", flow.Sleep(10*time.Second)).
151-
Step("Creating table", createTable).
182+
Step("wait for MySQL to be ready", retry.Do(time.Second, 60, checkMySQLConnection)).
183+
Step("Creating table", retry.Do(time.Second, 30, createTable(tableName))).
152184
Step(sidecar.Run("standardSidecar",
153185
append(componentRuntimeOptions(),
154186
embedded.WithoutApp(),
@@ -205,19 +237,10 @@ func TestMysqlNetworkError(t *testing.T) {
205237
return nil
206238
}
207239

208-
createTable := func(ctx flow.Context) error {
209-
db, err := sql.Open("mysql", dockerConnectionString)
210-
require.NoError(t, err)
211-
_, err = db.Exec("CREATE TABLE " + tableName + " (id INT, c1 TEXT, ts TIMESTAMP);")
212-
require.NoError(t, err)
213-
db.Close()
214-
return nil
215-
}
216-
217240
flow.New(t, "Run tests").
218241
Step(dockercompose.Run("db", dockerComposeYAML)).
219-
Step("wait for component to start", flow.Sleep(10*time.Second)).
220-
Step("Creating table", createTable).
242+
Step("wait for MySQL to be ready", retry.Do(time.Second, 60, checkMySQLConnection)).
243+
Step("Creating table", retry.Do(time.Second, 30, createTable(tableName))).
221244
Step(sidecar.Run("standardSidecar",
222245
append(componentRuntimeOptions(),
223246
embedded.WithoutApp(),
@@ -262,7 +285,7 @@ func TestMysqlExecEncoding(t *testing.T) {
262285
rowsAffected, exists := resp.Metadata["rows-affected"]
263286
require.True(ctx, exists, "rows-affected metadata should exist")
264287
assert.Equal(t, "1", rowsAffected, "rows-affected should be '1' for single INSERT")
265-
288+
266289
// Verify the encoding is correct (string, not number)
267290
// Parse to verify it's a valid integer string
268291
rowsCount, err := strconv.ParseInt(rowsAffected, 10, 64)
@@ -279,11 +302,11 @@ func TestMysqlExecEncoding(t *testing.T) {
279302
})
280303
require.NoError(ctx, err, "error in output binding - exec UPDATE")
281304
require.NotNil(ctx, resp, "response should not be nil")
282-
305+
283306
rowsAffected, exists = resp.Metadata["rows-affected"]
284307
require.True(ctx, exists, "rows-affected metadata should exist for UPDATE")
285308
assert.Equal(t, "1", rowsAffected, "rows-affected should be '1' for single UPDATE")
286-
309+
287310
// Verify encoding again
288311
rowsCount, err = strconv.ParseInt(rowsAffected, 10, 64)
289312
require.NoError(ctx, err, "rows-affected should be parseable as int64")
@@ -294,17 +317,17 @@ func TestMysqlExecEncoding(t *testing.T) {
294317
Name: "standard-binding",
295318
Operation: "exec",
296319
Metadata: map[string]string{
297-
"sql": fmt.Sprintf("INSERT INTO %s (id, c1, ts) VALUES (2, 'test2', '%s'), (3, 'test3', '%s');",
320+
"sql": fmt.Sprintf("INSERT INTO %s (id, c1, ts) VALUES (2, 'test2', '%s'), (3, 'test3', '%s');",
298321
tableName, time.Now().Format(mySQLDateTimeFormat), time.Now().Format(mySQLDateTimeFormat)),
299322
},
300323
})
301324
require.NoError(ctx, err, "error in output binding - exec multi-row INSERT")
302325
require.NotNil(ctx, resp, "response should not be nil")
303-
326+
304327
rowsAffected, exists = resp.Metadata["rows-affected"]
305328
require.True(ctx, exists, "rows-affected metadata should exist for multi-row INSERT")
306329
assert.Equal(t, "2", rowsAffected, "rows-affected should be '2' for two-row INSERT")
307-
330+
308331
// Verify encoding for multiple rows
309332
rowsCount, err = strconv.ParseInt(rowsAffected, 10, 64)
310333
require.NoError(ctx, err, "rows-affected should be parseable as int64")
@@ -320,11 +343,11 @@ func TestMysqlExecEncoding(t *testing.T) {
320343
})
321344
require.NoError(ctx, err, "error in output binding - exec DELETE")
322345
require.NotNil(ctx, resp, "response should not be nil")
323-
346+
324347
rowsAffected, exists = resp.Metadata["rows-affected"]
325348
require.True(ctx, exists, "rows-affected metadata should exist for DELETE")
326349
assert.Equal(t, "2", rowsAffected, "rows-affected should be '2' for two-row DELETE")
327-
350+
328351
// Verify encoding for DELETE
329352
rowsCount, err = strconv.ParseInt(rowsAffected, 10, 64)
330353
require.NoError(ctx, err, "rows-affected should be parseable as int64")
@@ -340,11 +363,11 @@ func TestMysqlExecEncoding(t *testing.T) {
340363
})
341364
require.NoError(ctx, err, "error in output binding - exec UPDATE with no match")
342365
require.NotNil(ctx, resp, "response should not be nil")
343-
366+
344367
rowsAffected, exists = resp.Metadata["rows-affected"]
345368
require.True(ctx, exists, "rows-affected metadata should exist even for zero rows")
346369
assert.Equal(t, "0", rowsAffected, "rows-affected should be '0' when no rows match")
347-
370+
348371
// Verify encoding for zero rows
349372
rowsCount, err = strconv.ParseInt(rowsAffected, 10, 64)
350373
require.NoError(ctx, err, "rows-affected should be parseable as int64")
@@ -371,19 +394,10 @@ func TestMysqlExecEncoding(t *testing.T) {
371394
return nil
372395
}
373396

374-
createTable := func(ctx flow.Context) error {
375-
db, err := sql.Open("mysql", dockerConnectionString)
376-
require.NoError(t, err)
377-
_, err = db.Exec("CREATE TABLE " + tableName + " (id INT, c1 TEXT, ts TIMESTAMP);")
378-
require.NoError(t, err)
379-
db.Close()
380-
return nil
381-
}
382-
383397
flow.New(t, "Run exec encoding tests").
384398
Step(dockercompose.Run("db", dockerComposeYAML)).
385-
Step("wait for component to start", flow.Sleep(10*time.Second)).
386-
Step("Creating table", createTable).
399+
Step("wait for MySQL to be ready", retry.Do(time.Second, 60, checkMySQLConnection)).
400+
Step("Creating table", retry.Do(time.Second, 30, createTable(tableName))).
387401
Step(sidecar.Run("standardSidecar",
388402
append(componentRuntimeOptions(),
389403
embedded.WithoutApp(),

0 commit comments

Comments
 (0)