...

Source file src/github.com/ory/x/popx/migrator_test.go

Documentation: github.com/ory/x/popx

     1  package popx
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"embed"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/gobuffalo/pop/v5"
    14  	"github.com/sirupsen/logrus"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  
    18  	"github.com/ory/x/logrusx"
    19  	"github.com/ory/x/pkgerx"
    20  	"github.com/ory/x/sqlcon/dockertest"
    21  )
    22  
    23  //go:embed stub/migrations/transactional/*.sql
    24  var transactionalMigrations embed.FS
    25  
    26  func TestMigratorUpgrading(t *testing.T) {
    27  	litedb, err := ioutil.TempFile(os.TempDir(), "sqlite-*")
    28  	require.NoError(t, err)
    29  	require.NoError(t, litedb.Close())
    30  
    31  	ctx := context.Background()
    32  
    33  	sqlite, err := pop.NewConnection(&pop.ConnectionDetails{
    34  		URL: "sqlite://file::memory:?_fk=true",
    35  	})
    36  	require.NoError(t, err)
    37  	require.NoError(t, sqlite.Open())
    38  
    39  	connections := map[string]*pop.Connection{
    40  		"sqlite": sqlite,
    41  	}
    42  
    43  	if !testing.Short() {
    44  		dockertest.Parallel([]func(){
    45  			func() {
    46  				connections["postgres"] = dockertest.ConnectToTestPostgreSQLPop(t)
    47  			},
    48  			func() {
    49  				connections["mysql"] = dockertest.ConnectToTestMySQLPop(t)
    50  			},
    51  			func() {
    52  				connections["cockroach"] = dockertest.ConnectToTestCockroachDBPop(t)
    53  			},
    54  		})
    55  	}
    56  
    57  	l := logrusx.New("", "", logrusx.ForceLevel(logrus.DebugLevel))
    58  
    59  	for name, c := range connections {
    60  		t.Run(fmt.Sprintf("database=%s", name), func(t *testing.T) {
    61  			legacy, err := pkgerx.NewMigrationBox("/popx/stub/migrations/legacy", c, l)
    62  			require.NoError(t, err)
    63  			require.NoError(t, legacy.Up())
    64  
    65  			var legacyStatusBuffer bytes.Buffer
    66  			require.NoError(t, legacy.Status(&legacyStatusBuffer))
    67  
    68  			legacyStatus := filterMySQL(t, name, legacyStatusBuffer.String())
    69  
    70  			require.NotContains(t, legacyStatus, Pending)
    71  
    72  			expected := legacy.DumpMigrationSchema()
    73  
    74  			transactional, err := NewMigrationBox(transactionalMigrations, NewMigrator(c, l, nil, 0))
    75  			require.NoError(t, err)
    76  
    77  			var transactionalStatusBuffer bytes.Buffer
    78  			statuses, err := transactional.Status(ctx)
    79  			require.NoError(t, err)
    80  
    81  			require.NoError(t, statuses.Write(&transactionalStatusBuffer))
    82  			transactionalStatus := filterMySQL(t, name, transactionalStatusBuffer.String())
    83  			require.NotContains(t, transactionalStatus, Pending)
    84  			require.False(t, statuses.HasPending())
    85  
    86  			require.NoError(t, transactional.Up(ctx))
    87  
    88  			actual := transactional.DumpMigrationSchema(ctx)
    89  			assert.EqualValues(t, expected, actual)
    90  
    91  			// Re-set and re-try
    92  
    93  			require.NoError(t, legacy.Down(-1))
    94  			require.NoError(t, transactional.Up(ctx))
    95  			actual = transactional.DumpMigrationSchema(ctx)
    96  			assert.EqualValues(t, expected, actual)
    97  		})
    98  	}
    99  }
   100  
   101  func filterMySQL(t *testing.T, name string, status string) string {
   102  	if name == "mysql" {
   103  		return status
   104  	}
   105  	// These only run for mysql and are thus expected to be pending:
   106  	//
   107  	// 20191100000005   identities                                Pending
   108  	// 20191100000009   verification                              Pending
   109  	// 20200519101058   create_recovery_addresses                 Pending
   110  	// 20200601101001   verification                              Pending
   111  
   112  	pending := []string{"20191100000005", "20191100000009", "20200519101058", "20200601101001"}
   113  	var lines []string
   114  	for _, l := range strings.Split(status, "\n") {
   115  		var skip bool
   116  		for _, p := range pending {
   117  			if strings.Contains(l, p) {
   118  				t.Logf("Removing expected pending line: %s", l)
   119  				skip = true
   120  				break
   121  			}
   122  		}
   123  		if !skip {
   124  			lines = append(lines, l)
   125  		}
   126  	}
   127  
   128  	return strings.Join(lines, "\n")
   129  }
   130  
   131  func TestMigratorUpgradingFromStart(t *testing.T) {
   132  	litedb, err := ioutil.TempFile(os.TempDir(), "sqlite-*")
   133  	require.NoError(t, err)
   134  	require.NoError(t, litedb.Close())
   135  
   136  	ctx := context.Background()
   137  
   138  	c, err := pop.NewConnection(&pop.ConnectionDetails{
   139  		URL: "sqlite://file::memory:?_fk=true",
   140  	})
   141  	require.NoError(t, err)
   142  	require.NoError(t, c.Open())
   143  
   144  	l := logrusx.New("", "", logrusx.ForceLevel(logrus.DebugLevel))
   145  	transactional, err := NewMigrationBox(transactionalMigrations, NewMigrator(c, l, nil, 0))
   146  	require.NoError(t, err)
   147  	status, err := transactional.Status(ctx)
   148  	require.NoError(t, err)
   149  	require.True(t, status.HasPending())
   150  
   151  	require.NoError(t, transactional.Up(ctx))
   152  
   153  	status, err = transactional.Status(ctx)
   154  	require.NoError(t, err)
   155  	require.False(t, status.HasPending())
   156  
   157  	// Are all the tables here?
   158  	var rows []string
   159  	require.NoError(t, c.Store.Select(&rows, "SELECT name FROM sqlite_master WHERE type='table'"))
   160  
   161  	for _, expected := range []string{
   162  		"schema_migration",
   163  		"identities",
   164  	} {
   165  		require.Contains(t, rows, expected)
   166  	}
   167  
   168  	require.NoError(t, transactional.Down(ctx, -1))
   169  }
   170  

View as plain text