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  }