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