@@ -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.
5051const 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+
5293func 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