github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/store/sqlstore/store_test.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package sqlstore
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"regexp"
    10  	"sync"
    11  	"testing"
    12  
    13  	"github.com/go-sql-driver/mysql"
    14  	"github.com/lib/pq"
    15  	"github.com/mattermost/gorp"
    16  	_ "github.com/mattn/go-sqlite3"
    17  	"github.com/pkg/errors"
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  
    21  	"github.com/mattermost/mattermost-server/v5/model"
    22  	"github.com/mattermost/mattermost-server/v5/store"
    23  	"github.com/mattermost/mattermost-server/v5/store/searchtest"
    24  	"github.com/mattermost/mattermost-server/v5/store/storetest"
    25  )
    26  
    27  type storeType struct {
    28  	Name        string
    29  	SqlSettings *model.SqlSettings
    30  	SqlStore    *SqlStore
    31  	Store       store.Store
    32  }
    33  
    34  var storeTypes []*storeType
    35  
    36  func newStoreType(name, driver string) *storeType {
    37  	return &storeType{
    38  		Name:        name,
    39  		SqlSettings: storetest.MakeSqlSettings(driver),
    40  	}
    41  }
    42  
    43  func StoreTest(t *testing.T, f func(*testing.T, store.Store)) {
    44  	defer func() {
    45  		if err := recover(); err != nil {
    46  			tearDownStores()
    47  			panic(err)
    48  		}
    49  	}()
    50  	for _, st := range storeTypes {
    51  		st := st
    52  		t.Run(st.Name, func(t *testing.T) {
    53  			if testing.Short() {
    54  				t.SkipNow()
    55  			}
    56  			f(t, st.Store)
    57  		})
    58  	}
    59  }
    60  
    61  func StoreTestWithSearchTestEngine(t *testing.T, f func(*testing.T, store.Store, *searchtest.SearchTestEngine)) {
    62  	defer func() {
    63  		if err := recover(); err != nil {
    64  			tearDownStores()
    65  			panic(err)
    66  		}
    67  	}()
    68  
    69  	for _, st := range storeTypes {
    70  		st := st
    71  		searchTestEngine := &searchtest.SearchTestEngine{
    72  			Driver: *st.SqlSettings.DriverName,
    73  		}
    74  
    75  		t.Run(st.Name, func(t *testing.T) { f(t, st.Store, searchTestEngine) })
    76  	}
    77  }
    78  
    79  func StoreTestWithSqlStore(t *testing.T, f func(*testing.T, store.Store, storetest.SqlStore)) {
    80  	defer func() {
    81  		if err := recover(); err != nil {
    82  			tearDownStores()
    83  			panic(err)
    84  		}
    85  	}()
    86  	for _, st := range storeTypes {
    87  		st := st
    88  		t.Run(st.Name, func(t *testing.T) {
    89  			if testing.Short() {
    90  				t.SkipNow()
    91  			}
    92  			f(t, st.Store, st.SqlStore)
    93  		})
    94  	}
    95  }
    96  
    97  func initStores() {
    98  	if testing.Short() {
    99  		return
   100  	}
   101  	// In CI, we already run the entire test suite for both mysql and postgres in parallel.
   102  	// So we just run the tests for the current database set.
   103  	if os.Getenv("IS_CI") == "true" {
   104  		switch os.Getenv("MM_SQLSETTINGS_DRIVERNAME") {
   105  		case "mysql":
   106  			storeTypes = append(storeTypes, newStoreType("MySQL", model.DATABASE_DRIVER_MYSQL))
   107  		case "postgres":
   108  			storeTypes = append(storeTypes, newStoreType("PostgreSQL", model.DATABASE_DRIVER_POSTGRES))
   109  		}
   110  	} else {
   111  		storeTypes = append(storeTypes, newStoreType("MySQL", model.DATABASE_DRIVER_MYSQL),
   112  			newStoreType("PostgreSQL", model.DATABASE_DRIVER_POSTGRES))
   113  	}
   114  
   115  	defer func() {
   116  		if err := recover(); err != nil {
   117  			tearDownStores()
   118  			panic(err)
   119  		}
   120  	}()
   121  	var wg sync.WaitGroup
   122  	for _, st := range storeTypes {
   123  		st := st
   124  		wg.Add(1)
   125  		go func() {
   126  			defer wg.Done()
   127  			st.SqlStore = New(*st.SqlSettings, nil)
   128  			st.Store = st.SqlStore
   129  			st.Store.DropAllTables()
   130  			st.Store.MarkSystemRanUnitTests()
   131  		}()
   132  	}
   133  	wg.Wait()
   134  }
   135  
   136  var tearDownStoresOnce sync.Once
   137  
   138  func tearDownStores() {
   139  	if testing.Short() {
   140  		return
   141  	}
   142  	tearDownStoresOnce.Do(func() {
   143  		var wg sync.WaitGroup
   144  		wg.Add(len(storeTypes))
   145  		for _, st := range storeTypes {
   146  			st := st
   147  			go func() {
   148  				if st.Store != nil {
   149  					st.Store.Close()
   150  				}
   151  				if st.SqlSettings != nil {
   152  					storetest.CleanupSqlSettings(st.SqlSettings)
   153  				}
   154  				wg.Done()
   155  			}()
   156  		}
   157  		wg.Wait()
   158  	})
   159  }
   160  
   161  // This test was used to consistently reproduce the race
   162  // before the fix in MM-28397.
   163  // Keeping it here to help avoiding future regressions.
   164  func TestStoreLicenseRace(t *testing.T) {
   165  	settings := makeSqlSettings(model.DATABASE_DRIVER_SQLITE)
   166  	settings.DataSourceReplicas = []string{":memory:"}
   167  	settings.DataSourceSearchReplicas = []string{":memory:"}
   168  	store := New(*settings, nil)
   169  
   170  	wg := sync.WaitGroup{}
   171  	wg.Add(3)
   172  
   173  	go func() {
   174  		store.UpdateLicense(&model.License{})
   175  		wg.Done()
   176  	}()
   177  
   178  	go func() {
   179  		store.GetReplica()
   180  		wg.Done()
   181  	}()
   182  
   183  	go func() {
   184  		store.GetSearchReplica()
   185  		wg.Done()
   186  	}()
   187  
   188  	wg.Wait()
   189  }
   190  
   191  func TestGetReplica(t *testing.T) {
   192  	t.Parallel()
   193  	testCases := []struct {
   194  		Description              string
   195  		DataSourceReplicas       []string
   196  		DataSourceSearchReplicas []string
   197  	}{
   198  		{
   199  			"no replicas",
   200  			[]string{},
   201  			[]string{},
   202  		},
   203  		{
   204  			"one source replica",
   205  			[]string{":memory:"},
   206  			[]string{},
   207  		},
   208  		{
   209  			"multiple source replicas",
   210  			[]string{":memory:", ":memory:", ":memory:"},
   211  			[]string{},
   212  		},
   213  		{
   214  			"one source search replica",
   215  			[]string{},
   216  			[]string{":memory:"},
   217  		},
   218  		{
   219  			"multiple source search replicas",
   220  			[]string{},
   221  			[]string{":memory:", ":memory:", ":memory:"},
   222  		},
   223  		{
   224  			"one source replica, one source search replica",
   225  			[]string{":memory:"},
   226  			[]string{":memory:"},
   227  		},
   228  		{
   229  			"one source replica, multiple source search replicas",
   230  			[]string{":memory:"},
   231  			[]string{":memory:", ":memory:", ":memory:"},
   232  		},
   233  		{
   234  			"multiple source replica, one source search replica",
   235  			[]string{":memory:", ":memory:", ":memory:"},
   236  			[]string{":memory:"},
   237  		},
   238  		{
   239  			"multiple source replica, multiple source search replicas",
   240  			[]string{":memory:", ":memory:", ":memory:"},
   241  			[]string{":memory:", ":memory:", ":memory:"},
   242  		},
   243  	}
   244  
   245  	for _, testCase := range testCases {
   246  		testCase := testCase
   247  		t.Run(testCase.Description+" with license", func(t *testing.T) {
   248  			t.Parallel()
   249  
   250  			settings := makeSqlSettings(model.DATABASE_DRIVER_SQLITE)
   251  			settings.DataSourceReplicas = testCase.DataSourceReplicas
   252  			settings.DataSourceSearchReplicas = testCase.DataSourceSearchReplicas
   253  			store := New(*settings, nil)
   254  			store.UpdateLicense(&model.License{})
   255  
   256  			replicas := make(map[*gorp.DbMap]bool)
   257  			for i := 0; i < 5; i++ {
   258  				replicas[store.GetReplica()] = true
   259  			}
   260  
   261  			searchReplicas := make(map[*gorp.DbMap]bool)
   262  			for i := 0; i < 5; i++ {
   263  				searchReplicas[store.GetSearchReplica()] = true
   264  			}
   265  
   266  			if len(testCase.DataSourceReplicas) > 0 {
   267  				// If replicas were defined, ensure none are the master.
   268  				assert.Len(t, replicas, len(testCase.DataSourceReplicas))
   269  
   270  				for replica := range replicas {
   271  					assert.NotSame(t, store.GetMaster(), replica)
   272  				}
   273  
   274  			} else if assert.Len(t, replicas, 1) {
   275  				// Otherwise ensure the replicas contains only the master.
   276  				for replica := range replicas {
   277  					assert.Same(t, store.GetMaster(), replica)
   278  				}
   279  			}
   280  
   281  			if len(testCase.DataSourceSearchReplicas) > 0 {
   282  				// If search replicas were defined, ensure none are the master nor the replicas.
   283  				assert.Len(t, searchReplicas, len(testCase.DataSourceSearchReplicas))
   284  
   285  				for searchReplica := range searchReplicas {
   286  					assert.NotSame(t, store.GetMaster(), searchReplica)
   287  					for replica := range replicas {
   288  						assert.NotSame(t, searchReplica, replica)
   289  					}
   290  				}
   291  			} else if len(testCase.DataSourceReplicas) > 0 {
   292  				assert.Equal(t, len(replicas), len(searchReplicas))
   293  				for k := range replicas {
   294  					assert.True(t, searchReplicas[k])
   295  				}
   296  			} else if len(testCase.DataSourceReplicas) == 0 && assert.Len(t, searchReplicas, 1) {
   297  				// Otherwise ensure the search replicas contains the master.
   298  				for searchReplica := range searchReplicas {
   299  					assert.Same(t, store.GetMaster(), searchReplica)
   300  				}
   301  			}
   302  		})
   303  
   304  		t.Run(testCase.Description+" without license", func(t *testing.T) {
   305  			t.Parallel()
   306  
   307  			settings := makeSqlSettings(model.DATABASE_DRIVER_SQLITE)
   308  			settings.DataSourceReplicas = testCase.DataSourceReplicas
   309  			settings.DataSourceSearchReplicas = testCase.DataSourceSearchReplicas
   310  			store := New(*settings, nil)
   311  
   312  			replicas := make(map[*gorp.DbMap]bool)
   313  			for i := 0; i < 5; i++ {
   314  				replicas[store.GetReplica()] = true
   315  			}
   316  
   317  			searchReplicas := make(map[*gorp.DbMap]bool)
   318  			for i := 0; i < 5; i++ {
   319  				searchReplicas[store.GetSearchReplica()] = true
   320  			}
   321  
   322  			if len(testCase.DataSourceReplicas) > 0 {
   323  				// If replicas were defined, ensure none are the master.
   324  				assert.Len(t, replicas, 1)
   325  
   326  				for replica := range replicas {
   327  					assert.Same(t, store.GetMaster(), replica)
   328  				}
   329  
   330  			} else if assert.Len(t, replicas, 1) {
   331  				// Otherwise ensure the replicas contains only the master.
   332  				for replica := range replicas {
   333  					assert.Same(t, store.GetMaster(), replica)
   334  				}
   335  			}
   336  
   337  			if len(testCase.DataSourceSearchReplicas) > 0 {
   338  				// If search replicas were defined, ensure none are the master nor the replicas.
   339  				assert.Len(t, searchReplicas, 1)
   340  
   341  				for searchReplica := range searchReplicas {
   342  					assert.Same(t, store.GetMaster(), searchReplica)
   343  				}
   344  
   345  			} else if len(testCase.DataSourceReplicas) > 0 {
   346  				assert.Equal(t, len(replicas), len(searchReplicas))
   347  				for k := range replicas {
   348  					assert.True(t, searchReplicas[k])
   349  				}
   350  			} else if assert.Len(t, searchReplicas, 1) {
   351  				// Otherwise ensure the search replicas contains the master.
   352  				for searchReplica := range searchReplicas {
   353  					assert.Same(t, store.GetMaster(), searchReplica)
   354  				}
   355  			}
   356  		})
   357  	}
   358  }
   359  
   360  func TestGetDbVersion(t *testing.T) {
   361  	testDrivers := []string{
   362  		model.DATABASE_DRIVER_POSTGRES,
   363  		model.DATABASE_DRIVER_MYSQL,
   364  		model.DATABASE_DRIVER_SQLITE,
   365  	}
   366  
   367  	for _, driver := range testDrivers {
   368  		t.Run("Should return db version for "+driver, func(t *testing.T) {
   369  			t.Parallel()
   370  			settings := makeSqlSettings(driver)
   371  			store := New(*settings, nil)
   372  
   373  			version, err := store.GetDbVersion(false)
   374  			require.NoError(t, err)
   375  			require.Regexp(t, regexp.MustCompile(`\d+\.\d+(\.\d+)?`), version)
   376  		})
   377  	}
   378  }
   379  
   380  func TestGetAllConns(t *testing.T) {
   381  	t.Parallel()
   382  	testCases := []struct {
   383  		Description              string
   384  		DataSourceReplicas       []string
   385  		DataSourceSearchReplicas []string
   386  		ExpectedNumConnections   int
   387  	}{
   388  		{
   389  			"no replicas",
   390  			[]string{},
   391  			[]string{},
   392  			1,
   393  		},
   394  		{
   395  			"one source replica",
   396  			[]string{":memory:"},
   397  			[]string{},
   398  			2,
   399  		},
   400  		{
   401  			"multiple source replicas",
   402  			[]string{":memory:", ":memory:", ":memory:"},
   403  			[]string{},
   404  			4,
   405  		},
   406  		{
   407  			"one source search replica",
   408  			[]string{},
   409  			[]string{":memory:"},
   410  			1,
   411  		},
   412  		{
   413  			"multiple source search replicas",
   414  			[]string{},
   415  			[]string{":memory:", ":memory:", ":memory:"},
   416  			1,
   417  		},
   418  		{
   419  			"one source replica, one source search replica",
   420  			[]string{":memory:"},
   421  			[]string{":memory:"},
   422  			2,
   423  		},
   424  		{
   425  			"one source replica, multiple source search replicas",
   426  			[]string{":memory:"},
   427  			[]string{":memory:", ":memory:", ":memory:"},
   428  			2,
   429  		},
   430  		{
   431  			"multiple source replica, one source search replica",
   432  			[]string{":memory:", ":memory:", ":memory:"},
   433  			[]string{":memory:"},
   434  			4,
   435  		},
   436  		{
   437  			"multiple source replica, multiple source search replicas",
   438  			[]string{":memory:", ":memory:", ":memory:"},
   439  			[]string{":memory:", ":memory:", ":memory:"},
   440  			4,
   441  		},
   442  	}
   443  
   444  	for _, testCase := range testCases {
   445  		testCase := testCase
   446  		t.Run(testCase.Description, func(t *testing.T) {
   447  			t.Parallel()
   448  			settings := makeSqlSettings(model.DATABASE_DRIVER_SQLITE)
   449  			settings.DataSourceReplicas = testCase.DataSourceReplicas
   450  			settings.DataSourceSearchReplicas = testCase.DataSourceSearchReplicas
   451  			store := New(*settings, nil)
   452  
   453  			assert.Len(t, store.GetAllConns(), testCase.ExpectedNumConnections)
   454  		})
   455  	}
   456  }
   457  
   458  func TestIsDuplicate(t *testing.T) {
   459  	testErrors := map[error]bool{
   460  		&pq.Error{Code: "42P06"}:                          false,
   461  		&pq.Error{Code: PGDupTableErrorCode}:              true,
   462  		&mysql.MySQLError{Number: uint16(1000)}:           false,
   463  		&mysql.MySQLError{Number: MySQLDupTableErrorCode}: true,
   464  		errors.New("Random error"):                        false,
   465  	}
   466  
   467  	for err, expected := range testErrors {
   468  		t.Run(fmt.Sprintf("Should return %t for %s", expected, err.Error()), func(t *testing.T) {
   469  			t.Parallel()
   470  			assert.Equal(t, expected, IsDuplicate(err))
   471  		})
   472  	}
   473  }
   474  
   475  func TestVersionString(t *testing.T) {
   476  	versions := []struct {
   477  		input  int
   478  		output string
   479  	}{
   480  		{
   481  			input:  100000,
   482  			output: "10.0",
   483  		},
   484  		{
   485  			input:  90603,
   486  			output: "9.603",
   487  		},
   488  		{
   489  			input:  120005,
   490  			output: "12.5",
   491  		},
   492  	}
   493  
   494  	for _, v := range versions {
   495  		out := VersionString(v.input)
   496  		assert.Equal(t, v.output, out)
   497  	}
   498  }
   499  
   500  func makeSqlSettings(driver string) *model.SqlSettings {
   501  	switch driver {
   502  	case model.DATABASE_DRIVER_POSTGRES:
   503  		return storetest.MakeSqlSettings(driver)
   504  	case model.DATABASE_DRIVER_MYSQL:
   505  		return storetest.MakeSqlSettings(driver)
   506  	case model.DATABASE_DRIVER_SQLITE:
   507  		return makeSqliteSettings()
   508  	}
   509  
   510  	return nil
   511  }
   512  
   513  func makeSqliteSettings() *model.SqlSettings {
   514  	driverName := model.DATABASE_DRIVER_SQLITE
   515  	dataSource := ":memory:"
   516  	maxIdleConns := 1
   517  	connMaxLifetimeMilliseconds := 3600000
   518  	connMaxIdleTimeMilliseconds := 300000
   519  	maxOpenConns := 1
   520  	queryTimeout := 5
   521  
   522  	return &model.SqlSettings{
   523  		DriverName:                  &driverName,
   524  		DataSource:                  &dataSource,
   525  		MaxIdleConns:                &maxIdleConns,
   526  		ConnMaxLifetimeMilliseconds: &connMaxLifetimeMilliseconds,
   527  		ConnMaxIdleTimeMilliseconds: &connMaxIdleTimeMilliseconds,
   528  		MaxOpenConns:                &maxOpenConns,
   529  		QueryTimeout:                &queryTimeout,
   530  	}
   531  }