github.com/mrqzzz/migrate@v5.1.7+incompatible/database/redshift/redshift_test.go (about) 1 package redshift 2 3 // error codes https://github.com/lib/pq/blob/master/error.go 4 5 import ( 6 "bytes" 7 "context" 8 "database/sql" 9 sqldriver "database/sql/driver" 10 "fmt" 11 "io" 12 "strconv" 13 "strings" 14 "testing" 15 ) 16 17 import ( 18 "github.com/dhui/dktest" 19 ) 20 21 import ( 22 dt "github.com/golang-migrate/migrate/v4/database/testing" 23 "github.com/golang-migrate/migrate/v4/dktesting" 24 ) 25 26 var ( 27 opts = dktest.Options{PortRequired: true, ReadyFunc: isReady} 28 specs = []dktesting.ContainerSpec{ 29 {ImageName: "postgres:8", Options: opts}, 30 } 31 ) 32 33 func redshiftConnectionString(host, port string) string { 34 return connectionString("redshift", host, port) 35 } 36 37 func pgConnectionString(host, port string) string { 38 return connectionString("postgres", host, port) 39 } 40 41 func connectionString(schema, host, port string) string { 42 return fmt.Sprintf("%s://postgres@%s:%s/postgres?sslmode=disable", schema, host, port) 43 } 44 45 func isReady(ctx context.Context, c dktest.ContainerInfo) bool { 46 ip, port, err := c.FirstPort() 47 if err != nil { 48 return false 49 } 50 51 db, err := sql.Open("postgres", pgConnectionString(ip, port)) 52 if err != nil { 53 return false 54 } 55 defer db.Close() 56 if err = db.PingContext(ctx); err != nil { 57 switch err { 58 case sqldriver.ErrBadConn, io.EOF: 59 return false 60 default: 61 fmt.Println(err) 62 } 63 return false 64 } 65 66 return true 67 } 68 69 func Test(t *testing.T) { 70 dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) { 71 ip, port, err := c.FirstPort() 72 if err != nil { 73 t.Fatal(err) 74 } 75 76 addr := redshiftConnectionString(ip, port) 77 p := &Redshift{} 78 d, err := p.Open(addr) 79 if err != nil { 80 t.Fatalf("%v", err) 81 } 82 defer d.Close() 83 dt.Test(t, d, []byte("SELECT 1")) 84 }) 85 } 86 87 func TestMultiStatement(t *testing.T) { 88 dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) { 89 ip, port, err := c.FirstPort() 90 if err != nil { 91 t.Fatal(err) 92 } 93 94 addr := redshiftConnectionString(ip, port) 95 p := &Redshift{} 96 d, err := p.Open(addr) 97 if err != nil { 98 t.Fatalf("%v", err) 99 } 100 defer d.Close() 101 if err := d.Run(bytes.NewReader([]byte("CREATE TABLE foo (foo text); CREATE TABLE bar (bar text);"))); err != nil { 102 t.Fatalf("expected err to be nil, got %v", err) 103 } 104 105 // make sure second table exists 106 var exists bool 107 if err := d.(*Redshift).conn.QueryRowContext(context.Background(), "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'bar' AND table_schema = (SELECT current_schema()))").Scan(&exists); err != nil { 108 t.Fatal(err) 109 } 110 if !exists { 111 t.Fatalf("expected table bar to exist") 112 } 113 }) 114 } 115 116 func TestErrorParsing(t *testing.T) { 117 dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) { 118 ip, port, err := c.FirstPort() 119 if err != nil { 120 t.Fatal(err) 121 } 122 123 addr := redshiftConnectionString(ip, port) 124 p := &Redshift{} 125 d, err := p.Open(addr) 126 if err != nil { 127 t.Fatalf("%v", err) 128 } 129 defer d.Close() 130 131 wantErr := `migration failed: syntax error at or near "TABLEE" (column 37) in line 1: CREATE TABLE foo ` + 132 `(foo text); CREATE TABLEE bar (bar text); (details: pq: syntax error at or near "TABLEE")` 133 if err := d.Run(bytes.NewReader([]byte("CREATE TABLE foo (foo text); CREATE TABLEE bar (bar text);"))); err == nil { 134 t.Fatal("expected err but got nil") 135 } else if err.Error() != wantErr { 136 t.Fatalf("expected '%s' but got '%s'", wantErr, err.Error()) 137 } 138 }) 139 } 140 141 func TestFilterCustomQuery(t *testing.T) { 142 dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) { 143 ip, port, err := c.FirstPort() 144 if err != nil { 145 t.Fatal(err) 146 } 147 148 addr := fmt.Sprintf("postgres://postgres@%v:%v/postgres?sslmode=disable&x-custom=foobar", ip, port) 149 p := &Redshift{} 150 d, err := p.Open(addr) 151 if err != nil { 152 t.Fatalf("%v", err) 153 } 154 defer d.Close() 155 }) 156 } 157 158 func TestWithSchema(t *testing.T) { 159 dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) { 160 ip, port, err := c.FirstPort() 161 if err != nil { 162 t.Fatal(err) 163 } 164 165 addr := redshiftConnectionString(ip, port) 166 p := &Redshift{} 167 d, err := p.Open(addr) 168 if err != nil { 169 t.Fatalf("%v", err) 170 } 171 defer d.Close() 172 173 // create foobar schema 174 if err := d.Run(bytes.NewReader([]byte("CREATE SCHEMA foobar AUTHORIZATION postgres"))); err != nil { 175 t.Fatal(err) 176 } 177 if err := d.SetVersion(1, false); err != nil { 178 t.Fatal(err) 179 } 180 181 // re-connect using that schema 182 d2, err := p.Open(fmt.Sprintf("postgres://postgres@%v:%v/postgres?sslmode=disable&search_path=foobar", ip, port)) 183 if err != nil { 184 t.Fatalf("%v", err) 185 } 186 defer d2.Close() 187 188 version, _, err := d2.Version() 189 if err != nil { 190 t.Fatal(err) 191 } 192 if version != -1 { 193 t.Fatal("expected NilVersion") 194 } 195 196 // now update version and compare 197 if err := d2.SetVersion(2, false); err != nil { 198 t.Fatal(err) 199 } 200 version, _, err = d2.Version() 201 if err != nil { 202 t.Fatal(err) 203 } 204 if version != 2 { 205 t.Fatal("expected version 2") 206 } 207 208 // meanwhile, the public schema still has the other version 209 version, _, err = d.Version() 210 if err != nil { 211 t.Fatal(err) 212 } 213 if version != 1 { 214 t.Fatal("expected version 2") 215 } 216 }) 217 } 218 219 func TestWithInstance(t *testing.T) { 220 221 } 222 223 func TestRedshift_Lock(t *testing.T) { 224 dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) { 225 ip, port, err := c.FirstPort() 226 if err != nil { 227 t.Fatal(err) 228 } 229 230 addr := pgConnectionString(ip, port) 231 p := &Redshift{} 232 d, err := p.Open(addr) 233 if err != nil { 234 t.Fatalf("%v", err) 235 } 236 237 dt.Test(t, d, []byte("SELECT 1")) 238 239 ps := d.(*Redshift) 240 241 err = ps.Lock() 242 if err != nil { 243 t.Fatal(err) 244 } 245 246 err = ps.Unlock() 247 if err != nil { 248 t.Fatal(err) 249 } 250 251 err = ps.Lock() 252 if err != nil { 253 t.Fatal(err) 254 } 255 256 err = ps.Unlock() 257 if err != nil { 258 t.Fatal(err) 259 } 260 }) 261 } 262 263 func Test_computeLineFromPos(t *testing.T) { 264 testcases := []struct { 265 pos int 266 wantLine uint 267 wantCol uint 268 input string 269 wantOk bool 270 }{ 271 { 272 15, 2, 6, "SELECT *\nFROM foo", true, // foo table does not exists 273 }, 274 { 275 16, 3, 6, "SELECT *\n\nFROM foo", true, // foo table does not exists, empty line 276 }, 277 { 278 25, 3, 7, "SELECT *\nFROM foo\nWHERE x", true, // x column error 279 }, 280 { 281 27, 5, 7, "SELECT *\n\nFROM foo\n\nWHERE x", true, // x column error, empty lines 282 }, 283 { 284 10, 2, 1, "SELECT *\nFROMM foo", true, // FROMM typo 285 }, 286 { 287 11, 3, 1, "SELECT *\n\nFROMM foo", true, // FROMM typo, empty line 288 }, 289 { 290 17, 2, 8, "SELECT *\nFROM foo", true, // last character 291 }, 292 { 293 18, 0, 0, "SELECT *\nFROM foo", false, // invalid position 294 }, 295 } 296 for i, tc := range testcases { 297 t.Run("tc"+strconv.Itoa(i), func(t *testing.T) { 298 run := func(crlf bool, nonASCII bool) { 299 var name string 300 if crlf { 301 name = "crlf" 302 } else { 303 name = "lf" 304 } 305 if nonASCII { 306 name += "-nonascii" 307 } else { 308 name += "-ascii" 309 } 310 t.Run(name, func(t *testing.T) { 311 input := tc.input 312 if crlf { 313 input = strings.Replace(input, "\n", "\r\n", -1) 314 } 315 if nonASCII { 316 input = strings.Replace(input, "FROM", "FRÖM", -1) 317 } 318 gotLine, gotCol, gotOK := computeLineFromPos(input, tc.pos) 319 320 if tc.wantOk { 321 t.Logf("pos %d, want %d:%d, %#v", tc.pos, tc.wantLine, tc.wantCol, input) 322 } 323 324 if gotOK != tc.wantOk { 325 t.Fatalf("expected ok %v but got %v", tc.wantOk, gotOK) 326 } 327 if gotLine != tc.wantLine { 328 t.Fatalf("expected line %d but got %d", tc.wantLine, gotLine) 329 } 330 if gotCol != tc.wantCol { 331 t.Fatalf("expected col %d but got %d", tc.wantCol, gotCol) 332 } 333 }) 334 } 335 run(false, false) 336 run(true, false) 337 run(false, true) 338 run(true, true) 339 }) 340 } 341 342 }