github.com/mrqzzz/migrate@v5.1.7+incompatible/database/mysql/mysql_test.go (about) 1 package mysql 2 3 import ( 4 "context" 5 "database/sql" 6 sqldriver "database/sql/driver" 7 "fmt" 8 "net/url" 9 "testing" 10 ) 11 12 import ( 13 "github.com/dhui/dktest" 14 "github.com/go-sql-driver/mysql" 15 ) 16 17 import ( 18 dt "github.com/golang-migrate/migrate/v4/database/testing" 19 "github.com/golang-migrate/migrate/v4/dktesting" 20 ) 21 22 const defaultPort = 3306 23 24 var ( 25 opts = dktest.Options{ 26 Env: map[string]string{"MYSQL_ROOT_PASSWORD": "root", "MYSQL_DATABASE": "public"}, 27 PortRequired: true, ReadyFunc: isReady, 28 } 29 // Supported versions: https://www.mysql.com/support/supportedplatforms/database.html 30 specs = []dktesting.ContainerSpec{ 31 {ImageName: "mysql:5.5", Options: opts}, 32 {ImageName: "mysql:5.6", Options: opts}, 33 {ImageName: "mysql:5.7", Options: opts}, 34 {ImageName: "mysql:8", Options: opts}, 35 } 36 ) 37 38 func isReady(ctx context.Context, c dktest.ContainerInfo) bool { 39 ip, port, err := c.Port(defaultPort) 40 if err != nil { 41 return false 42 } 43 44 db, err := sql.Open("mysql", fmt.Sprintf("root:root@tcp(%v:%v)/public", ip, port)) 45 if err != nil { 46 return false 47 } 48 defer db.Close() 49 if err = db.PingContext(ctx); err != nil { 50 switch err { 51 case sqldriver.ErrBadConn, mysql.ErrInvalidConn: 52 return false 53 default: 54 fmt.Println(err) 55 } 56 return false 57 } 58 59 return true 60 } 61 62 func Test(t *testing.T) { 63 // mysql.SetLogger(mysql.Logger(log.New(ioutil.Discard, "", log.Ltime))) 64 65 dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) { 66 ip, port, err := c.Port(defaultPort) 67 if err != nil { 68 t.Fatal(err) 69 } 70 71 addr := fmt.Sprintf("mysql://root:root@tcp(%v:%v)/public", ip, port) 72 p := &Mysql{} 73 d, err := p.Open(addr) 74 if err != nil { 75 t.Fatalf("%v", err) 76 } 77 defer d.Close() 78 dt.Test(t, d, []byte("SELECT 1")) 79 80 // check ensureVersionTable 81 if err := d.(*Mysql).ensureVersionTable(); err != nil { 82 t.Fatal(err) 83 } 84 // check again 85 if err := d.(*Mysql).ensureVersionTable(); err != nil { 86 t.Fatal(err) 87 } 88 }) 89 } 90 91 func TestLockWorks(t *testing.T) { 92 dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) { 93 ip, port, err := c.Port(defaultPort) 94 if err != nil { 95 t.Fatal(err) 96 } 97 98 addr := fmt.Sprintf("mysql://root:root@tcp(%v:%v)/public", ip, port) 99 p := &Mysql{} 100 d, err := p.Open(addr) 101 if err != nil { 102 t.Fatalf("%v", err) 103 } 104 dt.Test(t, d, []byte("SELECT 1")) 105 106 ms := d.(*Mysql) 107 108 err = ms.Lock() 109 if err != nil { 110 t.Fatal(err) 111 } 112 err = ms.Unlock() 113 if err != nil { 114 t.Fatal(err) 115 } 116 117 // make sure the 2nd lock works (RELEASE_LOCK is very finicky) 118 err = ms.Lock() 119 if err != nil { 120 t.Fatal(err) 121 } 122 err = ms.Unlock() 123 if err != nil { 124 t.Fatal(err) 125 } 126 }) 127 } 128 129 func TestURLToMySQLConfig(t *testing.T) { 130 testcases := []struct { 131 name string 132 urlStr string 133 expectedDSN string // empty string signifies that an error is expected 134 }{ 135 {name: "no user/password", urlStr: "mysql://tcp(127.0.0.1:3306)/myDB?multiStatements=true", 136 expectedDSN: "tcp(127.0.0.1:3306)/myDB?multiStatements=true"}, 137 {name: "only user", urlStr: "mysql://username@tcp(127.0.0.1:3306)/myDB?multiStatements=true", 138 expectedDSN: "username@tcp(127.0.0.1:3306)/myDB?multiStatements=true"}, 139 {name: "only user - with encoded :", 140 urlStr: "mysql://username%3A@tcp(127.0.0.1:3306)/myDB?multiStatements=true", 141 expectedDSN: "username:@tcp(127.0.0.1:3306)/myDB?multiStatements=true"}, 142 {name: "only user - with encoded @", 143 urlStr: "mysql://username%40@tcp(127.0.0.1:3306)/myDB?multiStatements=true", 144 expectedDSN: "username@@tcp(127.0.0.1:3306)/myDB?multiStatements=true"}, 145 {name: "user/password", urlStr: "mysql://username:password@tcp(127.0.0.1:3306)/myDB?multiStatements=true", 146 expectedDSN: "username:password@tcp(127.0.0.1:3306)/myDB?multiStatements=true"}, 147 // Not supported yet: https://github.com/go-sql-driver/mysql/issues/591 148 // {name: "user/password - user with encoded :", 149 // urlStr: "mysql://username%3A:password@tcp(127.0.0.1:3306)/myDB?multiStatements=true", 150 // expectedDSN: "username::pasword@tcp(127.0.0.1:3306)/myDB?multiStatements=true"}, 151 {name: "user/password - user with encoded @", 152 urlStr: "mysql://username%40:password@tcp(127.0.0.1:3306)/myDB?multiStatements=true", 153 expectedDSN: "username@:password@tcp(127.0.0.1:3306)/myDB?multiStatements=true"}, 154 {name: "user/password - password with encoded :", 155 urlStr: "mysql://username:password%3A@tcp(127.0.0.1:3306)/myDB?multiStatements=true", 156 expectedDSN: "username:password:@tcp(127.0.0.1:3306)/myDB?multiStatements=true"}, 157 {name: "user/password - password with encoded @", 158 urlStr: "mysql://username:password%40@tcp(127.0.0.1:3306)/myDB?multiStatements=true", 159 expectedDSN: "username:password@@tcp(127.0.0.1:3306)/myDB?multiStatements=true"}, 160 } 161 for _, tc := range testcases { 162 t.Run(tc.name, func(t *testing.T) { 163 u, err := url.Parse(tc.urlStr) 164 if err != nil { 165 t.Fatal("Failed to parse url string:", tc.urlStr, "error:", err) 166 } 167 if config, err := urlToMySQLConfig(*u); err == nil { 168 dsn := config.FormatDSN() 169 if dsn != tc.expectedDSN { 170 t.Error("Got unexpected DSN:", dsn, "!=", tc.expectedDSN) 171 } 172 } else { 173 if tc.expectedDSN != "" { 174 t.Error("Got unexpected error:", err, "urlStr:", tc.urlStr) 175 } 176 } 177 }) 178 } 179 }