diff --git a/Makefile b/Makefile index d5bf0f7ed5d..0bc56e3ad3d 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,6 @@ TESTFLAGSEE ?= -short # Packages lists TE_PACKAGES=$(shell go list ./...) -TE_PACKAGES_COMMA=$(shell echo $(TE_PACKAGES) | tr ' ' ',') # Plugins Packages PLUGIN_PACKAGES=mattermost-plugin-zoom mattermost-plugin-jira @@ -83,15 +82,16 @@ ifeq ($(BUILD_ENTERPRISE_READY),true) IGNORE:=$(shell cp $(BUILD_ENTERPRISE_DIR)/imports/imports.go imports/) IGNORE:=$(shell rm -f enterprise) IGNORE:=$(shell ln -s $(BUILD_ENTERPRISE_DIR) enterprise) +else + IGNORE:=$(shell rm -f imports/imports.go) endif EE_PACKAGES=$(shell go list ./enterprise/...) -EE_PACKAGES_COMMA=$(shell echo $(EE_PACKAGES) | tr ' ' ',') ifeq ($(BUILD_ENTERPRISE_READY),true) -ALL_PACKAGES_COMMA=$(TE_PACKAGES_COMMA),$(EE_PACKAGES_COMMA) +ALL_PACKAGES=$(TE_PACKAGES) $(EE_PACKAGES) else -ALL_PACKAGES_COMMA=$(TE_PACKAGES_COMMA) +ALL_PACKAGES=$(TE_PACKAGES) endif @@ -354,33 +354,6 @@ do-cover-file: ## Creates the test coverage report file. go-junit-report: go get -u github.com/jstemmer/go-junit-report -test-te: start-docker go-junit-report do-cover-file ## Runs tests in the team edition. - @echo Testing TE - @echo "Packages to test: "$(TE_PACKAGES) - find . -name 'cprofile*.out' -exec sh -c 'rm "{}"' \; - $(GO) test $(GOFLAGS) -run=$(TESTS) $(TESTFLAGS) -p 1 -v -timeout=2000s -covermode=count -coverpkg=$(ALL_PACKAGES_COMMA) -exec $(ROOT)/scripts/test-xprog.sh $(TE_PACKAGES) | tee output-test-te - cat output-test-te | $(GOPATH)/bin/go-junit-report > report-te.xml && rm output-test-te - find . -name 'cprofile*.out' -exec sh -c 'tail -n +2 {} >> cover.out ; rm "{}"' \; - -test-ee: start-docker go-junit-report do-cover-file ## Runs tests in the enterprise edition. - @echo Testing EE - - rm -f enterprise/config/*.crt - rm -f enterprise/config/*.key - -ifeq ($(BUILD_ENTERPRISE_READY),true) - @echo Testing EE - @echo "Packages to test: "$(EE_PACKAGES) - find . -name 'cprofile*.out' -exec sh -c 'rm "{}"' \; - $(GO) test $(GOFLAGS) -run=$(TESTS) $(TESTFLAGSEE) -p 1 -v -timeout=2000s -covermode=count -coverpkg=$(ALL_PACKAGES_COMMA) -exec $(ROOT)/scripts/test-xprog.sh $(EE_PACKAGES) 2>&1 | tee output-test-ee - cat output-test-ee | $(GOPATH)/bin/go-junit-report > report-ee.xml && rm output-test-ee - find . -name 'cprofile*.out' -exec sh -c 'tail -n +2 {} >> cover.out ; rm "{}"' \; - rm -f enterprise/config/*.crt - rm -f enterprise/config/*.key -else - @echo Skipping EE Tests -endif - test-compile: @echo COMPILE TESTS @@ -388,8 +361,13 @@ test-compile: $(GO) test $(GOFLAGS) -c $$package; \ done -test-server: test-te test-ee ## Runs tests. - find . -type d -name data -not -path './vendor/*' | xargs rm -rf +test-server: start-docker go-junit-report do-cover-file ## Runs tests. +ifeq ($(BUILD_ENTERPRISE_READY),true) + @echo Running all tests +else + @echo Running only TE tests +endif + ./scripts/test.sh "$(GO)" "$(GOFLAGS)" "$(ALL_PACKAGES)" "$(TESTS)" "$(TESTFLAGS)" internal-test-web-client: ## Runs web client tests. $(GO) run $(GOFLAGS) $(PLATFORM_FILES) test web_client_tests diff --git a/api4/api_test.go b/api4/api_test.go deleted file mode 100644 index 2efd21f22eb..00000000000 --- a/api4/api_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api4 - -import ( - "flag" - "os" - "testing" - - "github.com/mattermost/mattermost-server/mlog" - "github.com/mattermost/mattermost-server/store/storetest" - "github.com/mattermost/mattermost-server/utils" -) - -func TestMain(m *testing.M) { - flag.Parse() - - // Setup a global logger to catch tests logging outside of app context - // The global logger will be stomped by apps initalizing but that's fine for testing. Ideally this won't happen. - mlog.InitGlobalLogger(mlog.NewLogger(&mlog.LoggerConfiguration{ - EnableConsole: true, - ConsoleJson: true, - ConsoleLevel: "error", - EnableFile: false, - })) - - utils.TranslationsPreInit() - - // In the case where a dev just wants to run a single test, it's faster to just use the default - // store. - if filter := flag.Lookup("test.run").Value.String(); filter != "" && filter != "." { - mlog.Info("-test.run used, not creating temporary containers") - os.Exit(m.Run()) - } - - status := 0 - - container, settings, err := storetest.NewMySQLContainer() - if err != nil { - panic(err) - } - - UseTestStore(container, settings) - - defer func() { - StopTestStore() - os.Exit(status) - }() - - status = m.Run() -} diff --git a/api4/apitestlib.go b/api4/apitestlib.go index a9e099563e1..7dda6ded3c0 100644 --- a/api4/apitestlib.go +++ b/api4/apitestlib.go @@ -21,8 +21,6 @@ import ( "github.com/mattermost/mattermost-server/mlog" "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/store" - "github.com/mattermost/mattermost-server/store/sqlstore" - "github.com/mattermost/mattermost-server/store/storetest" "github.com/mattermost/mattermost-server/utils" "github.com/mattermost/mattermost-server/web" "github.com/mattermost/mattermost-server/wsapi" @@ -52,33 +50,18 @@ type TestHelper struct { tempWorkspace string } -type persistentTestStore struct { - store.Store -} +// testStore tracks the active test store. +// This is a bridge between the new testlib ownership of the test store and the existing usage +// of the api4 test helper by many packages. In the future, this test helper would ideally belong +// to the testlib altogether. +var testStore store.Store -func (*persistentTestStore) Close() {} - -var testStoreContainer *storetest.RunningContainer -var testStore *persistentTestStore - -// UseTestStore sets the container and corresponding settings to use for tests. Once the tests are -// complete (e.g. at the end of your TestMain implementation), you should call StopTestStore. -func UseTestStore(container *storetest.RunningContainer, settings *model.SqlSettings) { - testStoreContainer = container - testStore = &persistentTestStore{store.NewLayeredStore(sqlstore.NewSqlSupplier(*settings, nil), nil, nil)} -} - -func StopTestStore() { - if testStoreContainer != nil { - testStoreContainer.Stop() - testStoreContainer = nil - } +func UseTestStore(store store.Store) { + testStore = store } func setupTestHelper(enterprise bool, updateConfig func(*model.Config)) *TestHelper { - if testStore != nil { - testStore.DropAllTables() - } + testStore.DropAllTables() permConfig, err := os.Open(utils.FindConfigFile("config.json")) if err != nil { @@ -96,9 +79,7 @@ func setupTestHelper(enterprise bool, updateConfig func(*model.Config)) *TestHel } options := []app.Option{app.ConfigFile(tempConfig.Name()), app.DisableConfigWatch} - if testStore != nil { - options = append(options, app.StoreOverride(testStore)) - } + options = append(options, app.StoreOverride(testStore)) s, err := app.NewServer(options...) if err != nil { @@ -117,9 +98,7 @@ func setupTestHelper(enterprise bool, updateConfig func(*model.Config)) *TestHel cfg.EmailSettings.SendEmailNotifications = true }) prevListenAddress := *th.App.Config().ServiceSettings.ListenAddress - if testStore != nil { - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" }) - } + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" }) if updateConfig != nil { th.App.UpdateConfig(updateConfig) } @@ -205,7 +184,6 @@ func (me *TestHelper) TearDown() { utils.EnableDebugLogForTest() if err := recover(); err != nil { - StopTestStore() panic(err) } } diff --git a/api4/main_test.go b/api4/main_test.go new file mode 100644 index 00000000000..0829a8eeba5 --- /dev/null +++ b/api4/main_test.go @@ -0,0 +1,20 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package api4 + +import ( + "testing" + + "github.com/mattermost/mattermost-server/testlib" +) + +var mainHelper *testlib.MainHelper + +func TestMain(m *testing.M) { + mainHelper = testlib.NewMainHelper() + defer mainHelper.Close() + UseTestStore(mainHelper.Store) + + mainHelper.Main(m) +} diff --git a/api4/user.go b/api4/user.go index 6234e16d31c..735570a9640 100644 --- a/api4/user.go +++ b/api4/user.go @@ -603,7 +603,7 @@ func autocompleteUsers(c *Context, w http.ResponseWriter, r *http.Request) { } } - var autocomplete *model.UserAutocomplete + var autocomplete model.UserAutocomplete if len(channelId) > 0 { // Applying the provided teamId here is useful for DMs and GMs which don't belong diff --git a/app/app_test.go b/app/app_test.go index 00929401983..4ddec64c733 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -4,57 +4,14 @@ package app import ( - "flag" "fmt" - "os" "testing" "github.com/stretchr/testify/assert" - "github.com/mattermost/mattermost-server/mlog" "github.com/mattermost/mattermost-server/model" - "github.com/mattermost/mattermost-server/store/storetest" - "github.com/mattermost/mattermost-server/utils" ) -func TestMain(m *testing.M) { - flag.Parse() - - // Setup a global logger to catch tests logging outside of app context - // The global logger will be stomped by apps initalizing but that's fine for testing. Ideally this won't happen. - mlog.InitGlobalLogger(mlog.NewLogger(&mlog.LoggerConfiguration{ - EnableConsole: true, - ConsoleJson: true, - ConsoleLevel: "error", - EnableFile: false, - })) - - utils.TranslationsPreInit() - - // In the case where a dev just wants to run a single test, it's faster to just use the default - // store. - if filter := flag.Lookup("test.run").Value.String(); filter != "" && filter != "." { - mlog.Info("-test.run used, not creating temporary containers") - os.Exit(m.Run()) - } - - status := 0 - - container, settings, err := storetest.NewMySQLContainer() - if err != nil { - panic(err) - } - - UseTestStore(container, settings) - - defer func() { - StopTestStore() - os.Exit(status) - }() - - status = m.Run() -} - /* Temporarily comment out until MM-11108 func TestAppRace(t *testing.T) { for i := 0; i < 10; i++ { @@ -88,7 +45,7 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) { th := Setup() defer th.TearDown() - if testStoreSqlSupplier == nil { + if mainHelper.SqlSupplier == nil { t.Skip("This test requires a TestStore to be run.") } @@ -467,7 +424,7 @@ func TestDoEmojisPermissionsMigration(t *testing.T) { th := Setup() defer th.TearDown() - if testStoreSqlSupplier == nil { + if mainHelper.SqlSupplier == nil { t.Skip("This test requires a TestStore to be run.") } diff --git a/app/apptestlib.go b/app/helper_test.go similarity index 75% rename from app/apptestlib.go rename to app/helper_test.go index 04783294067..e277bc49f10 100644 --- a/app/apptestlib.go +++ b/app/helper_test.go @@ -13,12 +13,8 @@ import ( "testing" - "github.com/mattermost/mattermost-server/einterfaces" "github.com/mattermost/mattermost-server/mlog" "github.com/mattermost/mattermost-server/model" - "github.com/mattermost/mattermost-server/store" - "github.com/mattermost/mattermost-server/store/sqlstore" - "github.com/mattermost/mattermost-server/store/storetest" "github.com/mattermost/mattermost-server/utils" "github.com/mattermost/mattermost-server/utils/testutils" ) @@ -40,37 +36,8 @@ type TestHelper struct { MockedHTTPService *testutils.MockedHTTPService } -type persistentTestStore struct { - store.Store -} - -func (*persistentTestStore) Close() {} - -var testStoreContainer *storetest.RunningContainer -var testStore *persistentTestStore -var testStoreSqlSupplier *sqlstore.SqlSupplier -var testClusterInterface *FakeClusterInterface - -// UseTestStore sets the container and corresponding settings to use for tests. Once the tests are -// complete (e.g. at the end of your TestMain implementation), you should call StopTestStore. -func UseTestStore(container *storetest.RunningContainer, settings *model.SqlSettings) { - testClusterInterface = &FakeClusterInterface{} - testStoreContainer = container - testStoreSqlSupplier = sqlstore.NewSqlSupplier(*settings, nil) - testStore = &persistentTestStore{store.NewLayeredStore(testStoreSqlSupplier, nil, testClusterInterface)} -} - -func StopTestStore() { - if testStoreContainer != nil { - testStoreContainer.Stop() - testStoreContainer = nil - } -} - func setupTestHelper(enterprise bool) *TestHelper { - if testStore != nil { - testStore.DropAllTables() - } + mainHelper.Store.DropAllTables() permConfig, err := os.Open(utils.FindConfigFile("config.json")) if err != nil { @@ -88,9 +55,7 @@ func setupTestHelper(enterprise bool) *TestHelper { } options := []Option{ConfigFile(tempConfig.Name()), DisableConfigWatch} - if testStore != nil { - options = append(options, StoreOverride(testStore)) - } + options = append(options, StoreOverride(mainHelper.Store)) s, err := NewServer(options...) if err != nil { @@ -106,9 +71,7 @@ func setupTestHelper(enterprise bool) *TestHelper { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.MaxUsersPerTeam = 50 }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.RateLimitSettings.Enable = false }) prevListenAddress := *th.App.Config().ServiceSettings.ListenAddress - if testStore != nil { - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" }) - } + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" }) serverErr := th.App.StartServer() if serverErr != nil { panic(serverErr) @@ -446,7 +409,6 @@ func (me *TestHelper) TearDown() { me.ShutdownApp() os.Remove(me.tempConfigPath) if err := recover(); err != nil { - StopTestStore() panic(err) } if me.tempWorkspace != "" { @@ -455,25 +417,25 @@ func (me *TestHelper) TearDown() { } func (me *TestHelper) ResetRoleMigration() { - if _, err := testStoreSqlSupplier.GetMaster().Exec("DELETE from Roles"); err != nil { + if _, err := mainHelper.SqlSupplier.GetMaster().Exec("DELETE from Roles"); err != nil { panic(err) } - testClusterInterface.sendClearRoleCacheMessage() + mainHelper.ClusterInterface.SendClearRoleCacheMessage() - if _, err := testStoreSqlSupplier.GetMaster().Exec("DELETE from Systems where Name = :Name", map[string]interface{}{"Name": ADVANCED_PERMISSIONS_MIGRATION_KEY}); err != nil { + if _, err := mainHelper.SqlSupplier.GetMaster().Exec("DELETE from Systems where Name = :Name", map[string]interface{}{"Name": ADVANCED_PERMISSIONS_MIGRATION_KEY}); err != nil { panic(err) } } func (me *TestHelper) ResetEmojisMigration() { - if _, err := testStoreSqlSupplier.GetMaster().Exec("UPDATE Roles SET Permissions=REPLACE(Permissions, ', manage_emojis', '') WHERE builtin=True"); err != nil { + if _, err := mainHelper.SqlSupplier.GetMaster().Exec("UPDATE Roles SET Permissions=REPLACE(Permissions, ', manage_emojis', '') WHERE builtin=True"); err != nil { panic(err) } - testClusterInterface.sendClearRoleCacheMessage() + mainHelper.ClusterInterface.SendClearRoleCacheMessage() - if _, err := testStoreSqlSupplier.GetMaster().Exec("DELETE from Systems where Name = :Name", map[string]interface{}{"Name": EMOJIS_PERMISSIONS_MIGRATION_KEY}); err != nil { + if _, err := mainHelper.SqlSupplier.GetMaster().Exec("DELETE from Systems where Name = :Name", map[string]interface{}{"Name": EMOJIS_PERMISSIONS_MIGRATION_KEY}); err != nil { panic(err) } } @@ -533,36 +495,3 @@ func (me *TestHelper) SetupPluginAPI() *PluginAPI { return NewPluginAPI(me.App, manifest) } - -type FakeClusterInterface struct { - clusterMessageHandler einterfaces.ClusterMessageHandler -} - -func (me *FakeClusterInterface) StartInterNodeCommunication() {} -func (me *FakeClusterInterface) StopInterNodeCommunication() {} -func (me *FakeClusterInterface) RegisterClusterMessageHandler(event string, crm einterfaces.ClusterMessageHandler) { - me.clusterMessageHandler = crm -} -func (me *FakeClusterInterface) GetClusterId() string { return "" } -func (me *FakeClusterInterface) IsLeader() bool { return false } -func (me *FakeClusterInterface) GetMyClusterInfo() *model.ClusterInfo { return nil } -func (me *FakeClusterInterface) GetClusterInfos() []*model.ClusterInfo { return nil } -func (me *FakeClusterInterface) SendClusterMessage(cluster *model.ClusterMessage) {} -func (me *FakeClusterInterface) NotifyMsg(buf []byte) {} -func (me *FakeClusterInterface) GetClusterStats() ([]*model.ClusterStats, *model.AppError) { - return nil, nil -} -func (me *FakeClusterInterface) GetLogs(page, perPage int) ([]string, *model.AppError) { - return []string{}, nil -} -func (me *FakeClusterInterface) GetPluginStatuses() (model.PluginStatuses, *model.AppError) { - return nil, nil -} -func (me *FakeClusterInterface) ConfigChanged(previousConfig *model.Config, newConfig *model.Config, sendToOtherServer bool) *model.AppError { - return nil -} -func (me *FakeClusterInterface) sendClearRoleCacheMessage() { - me.clusterMessageHandler(&model.ClusterMessage{ - Event: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES, - }) -} diff --git a/app/main_test.go b/app/main_test.go new file mode 100644 index 00000000000..b12c72d4621 --- /dev/null +++ b/app/main_test.go @@ -0,0 +1,19 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package app + +import ( + "testing" + + "github.com/mattermost/mattermost-server/testlib" +) + +var mainHelper *testlib.MainHelper + +func TestMain(m *testing.M) { + mainHelper = testlib.NewMainHelper() + defer mainHelper.Close() + + mainHelper.Main(m) +} diff --git a/build/legacy.mk b/build/legacy.mk new file mode 100644 index 00000000000..ff07c654fec --- /dev/null +++ b/build/legacy.mk @@ -0,0 +1,5 @@ +# test-te used to just run the team edition tests, but now runs whatever is available +test-te: test-server + +# test-ee used to just run the enterprise edition tests, but now runs whatever is available +test-ee: test-server diff --git a/cmd/mattermost/commands/channel_test.go b/cmd/mattermost/commands/channel_test.go index 4d1fc8d43e7..5ac338b6684 100644 --- a/cmd/mattermost/commands/channel_test.go +++ b/cmd/mattermost/commands/channel_test.go @@ -8,50 +8,49 @@ import ( "testing" "time" - "github.com/mattermost/mattermost-server/api4" "github.com/mattermost/mattermost-server/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestJoinChannel(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() channel := th.CreatePublicChannel() - CheckCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email) + th.CheckCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email) // Joining twice should succeed - CheckCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email) + th.CheckCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email) // should fail because channel does not exist - require.Error(t, RunCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name+"asdf", th.BasicUser2.Email)) + require.Error(t, th.RunCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name+"asdf", th.BasicUser2.Email)) } func TestRemoveChannel(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() channel := th.CreatePublicChannel() - CheckCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email) + th.CheckCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email) // should fail because channel does not exist - require.Error(t, RunCommand(t, "channel", "remove", th.BasicTeam.Name+":doesnotexist", th.BasicUser2.Email)) + require.Error(t, th.RunCommand(t, "channel", "remove", th.BasicTeam.Name+":doesnotexist", th.BasicUser2.Email)) time.Sleep(time.Second) - CheckCommand(t, "channel", "remove", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email) + th.CheckCommand(t, "channel", "remove", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email) time.Sleep(time.Second) // Leaving twice should succeed - CheckCommand(t, "channel", "remove", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email) + th.CheckCommand(t, "channel", "remove", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email) } func TestMoveChannel(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() team1 := th.BasicTeam @@ -68,22 +67,22 @@ func TestMoveChannel(t *testing.T) { origin := team1.Name + ":" + channel.Name dest := team2.Name - CheckCommand(t, "channel", "add", origin, adminEmail) + th.CheckCommand(t, "channel", "add", origin, adminEmail) // should fail with nil because errors are logged instead of returned when a channel does not exist - CheckCommand(t, "channel", "move", dest, team1.Name+":doesnotexist", "--username", adminUsername) + th.CheckCommand(t, "channel", "move", dest, team1.Name+":doesnotexist", "--username", adminUsername) - CheckCommand(t, "channel", "move", dest, origin, "--username", adminUsername) + th.CheckCommand(t, "channel", "move", dest, origin, "--username", adminUsername) } func TestListChannels(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() channel := th.CreatePublicChannel() th.Client.Must(th.Client.DeleteChannel(channel.Id)) - output := CheckCommand(t, "channel", "list", th.BasicTeam.Name) + output := th.CheckCommand(t, "channel", "list", th.BasicTeam.Name) if !strings.Contains(string(output), "town-square") { t.Fatal("should have channels") @@ -95,37 +94,37 @@ func TestListChannels(t *testing.T) { } func TestRestoreChannel(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() channel := th.CreatePublicChannel() th.Client.Must(th.Client.DeleteChannel(channel.Id)) - CheckCommand(t, "channel", "restore", th.BasicTeam.Name+":"+channel.Name) + th.CheckCommand(t, "channel", "restore", th.BasicTeam.Name+":"+channel.Name) // restoring twice should succeed - CheckCommand(t, "channel", "restore", th.BasicTeam.Name+":"+channel.Name) + th.CheckCommand(t, "channel", "restore", th.BasicTeam.Name+":"+channel.Name) } func TestCreateChannel(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() id := model.NewId() name := "name" + id - CheckCommand(t, "channel", "create", "--display_name", name, "--team", th.BasicTeam.Name, "--name", name) + th.CheckCommand(t, "channel", "create", "--display_name", name, "--team", th.BasicTeam.Name, "--name", name) name = name + "-private" - CheckCommand(t, "channel", "create", "--display_name", name, "--team", th.BasicTeam.Name, "--private", "--name", name) + th.CheckCommand(t, "channel", "create", "--display_name", name, "--team", th.BasicTeam.Name, "--private", "--name", name) } func TestRenameChannel(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() channel := th.CreatePublicChannel() - CheckCommand(t, "channel", "rename", th.BasicTeam.Name+":"+channel.Name, "newchannelname10", "--display_name", "New Display Name") + th.CheckCommand(t, "channel", "rename", th.BasicTeam.Name+":"+channel.Name, "newchannelname10", "--display_name", "New Display Name") // Get the channel from the DB updatedChannel, _ := th.App.GetChannel(channel.Id) diff --git a/cmd/mattermost/commands/cmdtestlib.go b/cmd/mattermost/commands/cmdtestlib.go index 93dcc956602..24ae39a5c4b 100644 --- a/cmd/mattermost/commands/cmdtestlib.go +++ b/cmd/mattermost/commands/cmdtestlib.go @@ -4,8 +4,11 @@ package commands import ( + "bytes" "flag" "fmt" + "io" + "io/ioutil" "os" "os/exec" "path/filepath" @@ -13,33 +16,159 @@ import ( "testing" "github.com/stretchr/testify/require" + + "github.com/mattermost/mattermost-server/api4" + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/testlib" ) var coverprofileCounters map[string]int = make(map[string]int) -func execArgs(t *testing.T, args []string) []string { - ret := []string{"-test.run", "ExecCommand"} +var mainHelper *testlib.MainHelper + +type testHelper struct { + *api4.TestHelper + + config *model.Config + tempDir string + configFilePath string + disableAutoConfig bool +} + +// Setup creates an instance of testHelper. +func Setup() *testHelper { + dir, err := ioutil.TempDir("", "testHelper") + if err != nil { + panic("failed to create temporary directory: " + err.Error()) + } + + api4TestHelper := api4.Setup() + + testHelper := &testHelper{ + TestHelper: api4TestHelper, + tempDir: dir, + configFilePath: filepath.Join(dir, "config-helper.json"), + } + + config := &model.Config{} + config.SetDefaults() + testHelper.SetConfig(config) + + return testHelper +} + +// InitBasic simply proxies to api4.InitBasic, while still returning a testHelper. +func (h *testHelper) InitBasic() *testHelper { + h.TestHelper.InitBasic() + return h +} + +// TemporaryDirectory returns the temporary directory created for user by the test helper. +func (h *testHelper) TemporaryDirectory() string { + return h.tempDir +} + +// Config returns the configuration passed to a running command. +func (h *testHelper) Config() *model.Config { + return h.config.Clone() +} + +// ConfigPath returns the path to the temporary config file passed to a running command. +func (h *testHelper) ConfigPath() string { + return h.configFilePath +} + +// SetConfig replaces the configuration passed to a running command. +func (h *testHelper) SetConfig(config *model.Config) { + config.SqlSettings = *mainHelper.Settings + h.config = config + + if err := ioutil.WriteFile(h.configFilePath, []byte(config.ToJson()), 0600); err != nil { + panic("failed to write file " + h.configFilePath + ": " + err.Error()) + } +} + +// SetAutoConfig configures whether the --config flag is automatically passed to a running command. +func (h *testHelper) SetAutoConfig(autoConfig bool) { + h.disableAutoConfig = !autoConfig +} + +// TearDown cleans up temporary files and assets created during the life of the test helper. +func (h *testHelper) TearDown() { + h.TestHelper.TearDown() + os.RemoveAll(h.tempDir) +} + +func (h *testHelper) execArgs(t *testing.T, args []string) []string { + ret := []string{"-test.v", "-test.run", "ExecCommand"} if coverprofile := flag.Lookup("test.coverprofile").Value.String(); coverprofile != "" { dir := filepath.Dir(coverprofile) base := filepath.Base(coverprofile) baseParts := strings.SplitN(base, ".", 2) - coverprofileCounters[t.Name()] = coverprofileCounters[t.Name()] + 1 - baseParts[0] = fmt.Sprintf("%v-%v-%v", baseParts[0], t.Name(), coverprofileCounters[t.Name()]) + name := strings.Replace(t.Name(), "/", "_", -1) + coverprofileCounters[name] = coverprofileCounters[name] + 1 + baseParts[0] = fmt.Sprintf("%v-%v-%v", baseParts[0], name, coverprofileCounters[name]) ret = append(ret, "-test.coverprofile", filepath.Join(dir, strings.Join(baseParts, "."))) } - return append(append(ret, "--", "--disableconfigwatch"), args...) + + ret = append(ret, "--", "--disableconfigwatch") + + // Unless the test passes a `--config` of its own, create a temporary one from the default + // configuration with the current test database applied. + hasConfig := h.disableAutoConfig + for _, arg := range args { + if arg == "--config" { + hasConfig = true + break + } + } + + if !hasConfig { + ret = append(ret, "--config", h.configFilePath) + } + + ret = append(ret, args...) + + return ret } -func CheckCommand(t *testing.T, args ...string) string { +// CheckCommand invokes the test binary, returning the output modified for assertion testing. +func (h *testHelper) CheckCommand(t *testing.T, args ...string) string { path, err := os.Executable() require.NoError(t, err) - output, err := exec.Command(path, execArgs(t, args)...).CombinedOutput() + output, err := exec.Command(path, h.execArgs(t, args)...).CombinedOutput() require.NoError(t, err, string(output)) return strings.TrimSpace(strings.TrimSuffix(strings.TrimSpace(string(output)), "PASS")) } -func RunCommand(t *testing.T, args ...string) error { +// RunCommand invokes the test binary, returning only any error. +func (h *testHelper) RunCommand(t *testing.T, args ...string) error { path, err := os.Executable() require.NoError(t, err) - return exec.Command(path, execArgs(t, args)...).Run() + return exec.Command(path, h.execArgs(t, args)...).Run() +} + +// RunCommandWithOutput is a variant of RunCommand that returns the unmodified output and any error. +func (h *testHelper) RunCommandWithOutput(t *testing.T, args ...string) (string, error) { + path, err := os.Executable() + require.NoError(t, err) + + cmd := exec.Command(path, h.execArgs(t, args)...) + + var buf bytes.Buffer + reader, writer := io.Pipe() + cmd.Stdout = writer + cmd.Stderr = writer + + done := make(chan bool) + go func() { + io.Copy(&buf, reader) + close(done) + }() + + err = cmd.Run() + writer.Close() + <-done + + return buf.String(), err } diff --git a/cmd/mattermost/commands/command_test.go b/cmd/mattermost/commands/command_test.go index 5ce16fb772b..878a1af0b9b 100644 --- a/cmd/mattermost/commands/command_test.go +++ b/cmd/mattermost/commands/command_test.go @@ -4,18 +4,21 @@ package commands import ( - "os" - "os/exec" "testing" - "github.com/mattermost/mattermost-server/api4" + "github.com/mattermost/mattermost-server/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestCreateCommand(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() + + config := th.Config() + *config.ServiceSettings.EnableCommands = true + th.SetConfig(config) + team := th.BasicTeam adminUser := th.TeamAdminUser user := th.BasicUser @@ -107,13 +110,9 @@ func TestCreateCommand(t *testing.T) { }, } - path, err := os.Executable() - require.NoError(t, err) - for _, testCase := range testCases { t.Run(testCase.Description, func(t *testing.T) { - - actual, _ := exec.Command(path, execArgs(t, testCase.Args)...).CombinedOutput() + actual, _ := th.RunCommandWithOutput(t, testCase.Args...) cmds, _ := th.SystemAdminClient.ListCommands(team.Id, true) @@ -132,9 +131,11 @@ func TestCreateCommand(t *testing.T) { } } -/* Commenting it out because race condition func TestDeleteCommand(t *testing.T) { - th := app.Setup().InitBasic() + // Skipped due to v5.6 RC build issues. + t.Skip() + + th := Setup().InitBasic() defer th.TearDown() url := "http://localhost:8000/test-command" team := th.BasicTeam @@ -158,14 +159,13 @@ func TestDeleteCommand(t *testing.T) { require.Nil(t, err) assert.Equal(t, len(commands), 1) - CheckCommand(t, "command", "delete", command.Id) + th.CheckCommand(t, "command", "delete", command.Id) commands, err = th.App.ListTeamCommands(team.Id) require.Nil(t, err) assert.Equal(t, len(commands), 0) }) t.Run("not existing command", func(t *testing.T) { - assert.Error(t, RunCommand(t, "command", "delete", "invalid")) + assert.Error(t, th.RunCommand(t, "command", "delete", "invalid")) }) } -*/ diff --git a/cmd/mattermost/commands/config_flag_test.go b/cmd/mattermost/commands/config_flag_test.go index 00a817448d0..1ccbb9685e3 100644 --- a/cmd/mattermost/commands/config_flag_test.go +++ b/cmd/mattermost/commands/config_flag_test.go @@ -4,6 +4,7 @@ package commands import ( + "encoding/json" "io/ioutil" "os" "path/filepath" @@ -11,21 +12,13 @@ import ( "github.com/stretchr/testify/require" - "encoding/json" - "github.com/mattermost/mattermost-server/utils" ) func TestConfigFlag(t *testing.T) { - dir, err := ioutil.TempDir("", "") - require.NoError(t, err) - defer os.RemoveAll(dir) - - utils.TranslationsPreInit() - config, _, _, err := utils.LoadConfig("config.json") - require.Nil(t, err) - configPath := filepath.Join(dir, "foo.json") - require.NoError(t, ioutil.WriteFile(configPath, []byte(config.ToJson()), 0600)) + th := Setup() + defer th.TearDown() + dir := th.TemporaryDirectory() timezones := utils.LoadTimezones("timezones.json") tzConfigPath := filepath.Join(dir, "timezones.json") @@ -41,8 +34,15 @@ func TestConfigFlag(t *testing.T) { defer os.Chdir(prevDir) os.Chdir(dir) - require.Error(t, RunCommand(t, "version")) - CheckCommand(t, "--config", "foo.json", "version") - CheckCommand(t, "--config", "./foo.json", "version") - CheckCommand(t, "--config", configPath, "version") + t.Run("version without a config file should fail", func(t *testing.T) { + th.SetAutoConfig(false) + defer th.SetAutoConfig(true) + require.Error(t, th.RunCommand(t, "version")) + }) + + t.Run("version with varying paths to the config file", func(t *testing.T) { + th.CheckCommand(t, "--config", filepath.Base(th.ConfigPath()), "version") + th.CheckCommand(t, "--config", "./"+filepath.Base(th.ConfigPath()), "version") + th.CheckCommand(t, "version") + }) } diff --git a/cmd/mattermost/commands/config_test.go b/cmd/mattermost/commands/config_test.go index dbaf5803a00..e974d8fe8b7 100644 --- a/cmd/mattermost/commands/config_test.go +++ b/cmd/mattermost/commands/config_test.go @@ -4,16 +4,12 @@ package commands import ( - "io/ioutil" - "os" - "path/filepath" "reflect" "sort" "strings" "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "github.com/mattermost/mattermost-server/model" ) @@ -72,88 +68,90 @@ type TestNewTeamSettings struct { } func TestConfigValidate(t *testing.T) { - dir, err := ioutil.TempDir("", "") - require.NoError(t, err) - defer os.RemoveAll(dir) + th := Setup() + defer th.TearDown() - path := filepath.Join(dir, "config.json") - config := &model.Config{} - config.SetDefaults() - require.NoError(t, ioutil.WriteFile(path, []byte(config.ToJson()), 0600)) - - assert.Error(t, RunCommand(t, "--config", "foo.json", "config", "validate")) - CheckCommand(t, "--config", path, "config", "validate") + assert.Error(t, th.RunCommand(t, "--config", "foo.json", "config", "validate")) + th.CheckCommand(t, "config", "validate") } func TestConfigGet(t *testing.T) { - // Error when no arguments are given - assert.Error(t, RunCommand(t, "config", "get")) + th := Setup() + defer th.TearDown() - // Error when more than one config settings are given - assert.Error(t, RunCommand(t, "config", "get", "abc", "def")) + t.Run("Error when no arguments are given", func(t *testing.T) { + assert.Error(t, th.RunCommand(t, "config", "get")) + }) - // Error when a config setting which is not in the config.json is given - assert.Error(t, RunCommand(t, "config", "get", "abc")) + t.Run("Error when more than one config settings are given", func(t *testing.T) { + assert.Error(t, th.RunCommand(t, "config", "get", "abc", "def")) + }) - // No Error when a config setting which is in the config.json is given - CheckCommand(t, "config", "get", "MessageExportSettings") - CheckCommand(t, "config", "get", "MessageExportSettings.GlobalRelaySettings") - CheckCommand(t, "config", "get", "MessageExportSettings.GlobalRelaySettings.CustomerType") + t.Run("Error when a config setting which is not in the config.json is given", func(t *testing.T) { + assert.Error(t, th.RunCommand(t, "config", "get", "abc")) + }) - // check output - output := CheckCommand(t, "config", "get", "MessageExportSettings") + t.Run("No Error when a config setting which is in the config.json is given", func(t *testing.T) { + th.CheckCommand(t, "config", "get", "MessageExportSettings") + th.CheckCommand(t, "config", "get", "MessageExportSettings.GlobalRelaySettings") + th.CheckCommand(t, "config", "get", "MessageExportSettings.GlobalRelaySettings.CustomerType") + }) - assert.Contains(t, string(output), "EnableExport") - assert.Contains(t, string(output), "ExportFormat") - assert.Contains(t, string(output), "DailyRunTime") - assert.Contains(t, string(output), "ExportFromTimestamp") + t.Run("check output", func(t *testing.T) { + output := th.CheckCommand(t, "config", "get", "MessageExportSettings") + + assert.Contains(t, string(output), "EnableExport") + assert.Contains(t, string(output), "ExportFormat") + assert.Contains(t, string(output), "DailyRunTime") + assert.Contains(t, string(output), "ExportFromTimestamp") + }) } func TestConfigSet(t *testing.T) { - dir, err := ioutil.TempDir("", "") - require.NoError(t, err) - defer os.RemoveAll(dir) + th := Setup() + defer th.TearDown() - path := filepath.Join(dir, "config.json") - config := &model.Config{} - config.SetDefaults() - require.NoError(t, ioutil.WriteFile(path, []byte(config.ToJson()), 0600)) + t.Run("Error when no arguments are given", func(t *testing.T) { + assert.Error(t, th.RunCommand(t, "config", "set")) + }) - // Error when no arguments are given - assert.Error(t, RunCommand(t, "--config", path, "config", "set")) + t.Run("Error when only one argument is given", func(t *testing.T) { + assert.Error(t, th.RunCommand(t, "config", "set", "test")) + }) - // Error when only one argument is given - assert.Error(t, RunCommand(t, "--config", path, "config", "set", "test")) + t.Run("Error when the wrong key is set", func(t *testing.T) { + assert.Error(t, th.RunCommand(t, "config", "set", "invalid-key", "value")) + assert.Error(t, th.RunCommand(t, "config", "get", "invalid-key")) + }) - // Error when the wrong key is set - assert.Error(t, RunCommand(t, "--config", path, "config", "set", "invalid-key", "value")) - assert.Error(t, RunCommand(t, "--config", path, "config", "get", "invalid-key")) + t.Run("Error when the wrong value is set", func(t *testing.T) { + assert.Error(t, th.RunCommand(t, "config", "set", "EmailSettings.ConnectionSecurity", "invalid")) + output := th.CheckCommand(t, "config", "get", "EmailSettings.ConnectionSecurity") + assert.NotContains(t, string(output), "invalid") + }) - // Error when the wrong value is set - assert.Error(t, RunCommand(t, "--config", path, "config", "set", "EmailSettings.ConnectionSecurity", "invalid")) - output := CheckCommand(t, "--config", path, "config", "get", "EmailSettings.ConnectionSecurity") - assert.NotContains(t, string(output), "invalid") + t.Run("Error when the wrong locale is set", func(t *testing.T) { + th.CheckCommand(t, "config", "set", "LocalizationSettings.DefaultServerLocale", "es") + assert.Error(t, th.RunCommand(t, "config", "set", "LocalizationSettings.DefaultServerLocale", "invalid")) + output := th.CheckCommand(t, "config", "get", "LocalizationSettings.DefaultServerLocale") + assert.NotContains(t, string(output), "invalid") + assert.NotContains(t, string(output), "\"en\"") + }) - // Error when the wrong locale is set - CheckCommand(t, "--config", path, "config", "set", "LocalizationSettings.DefaultServerLocale", "es") - assert.Error(t, RunCommand(t, "--config", path, "config", "set", "LocalizationSettings.DefaultServerLocale", "invalid")) - output = CheckCommand(t, "--config", path, "config", "get", "LocalizationSettings.DefaultServerLocale") - assert.NotContains(t, string(output), "invalid") - assert.NotContains(t, string(output), "\"en\"") + t.Run("Success when a valid value is set", func(t *testing.T) { + assert.NoError(t, th.RunCommand(t, "config", "set", "EmailSettings.ConnectionSecurity", "TLS")) + output := th.CheckCommand(t, "config", "get", "EmailSettings.ConnectionSecurity") + assert.Contains(t, string(output), "TLS") + }) - // Success when a valid value is set - CheckCommand(t, "--config", path, "config", "set", "EmailSettings.ConnectionSecurity", "TLS") - output = CheckCommand(t, "--config", path, "config", "get", "EmailSettings.ConnectionSecurity") - assert.Contains(t, string(output), "TLS") - - // Success when a valid locale is set - CheckCommand(t, "--config", path, "config", "set", "LocalizationSettings.DefaultServerLocale", "es") - output = CheckCommand(t, "--config", path, "config", "get", "LocalizationSettings.DefaultServerLocale") - assert.Contains(t, string(output), "\"es\"") + t.Run("Success when a valid locale is set", func(t *testing.T) { + assert.NoError(t, th.RunCommand(t, "config", "set", "LocalizationSettings.DefaultServerLocale", "es")) + output := th.CheckCommand(t, "config", "get", "LocalizationSettings.DefaultServerLocale") + assert.Contains(t, string(output), "\"es\"") + }) } func TestStructToMap(t *testing.T) { - cases := []struct { Name string Input interface{} @@ -324,7 +322,6 @@ func TestConfigToMap(t *testing.T) { } func TestPrintMap(t *testing.T) { - inputCases := []interface{}{ map[string]interface{}{ "CustomerType": "A9", @@ -387,7 +384,6 @@ func TestPrintMap(t *testing.T) { } func TestPrintConfigValues(t *testing.T) { - outputs := []string{ "Siteurl: \"abc\"\nWebsocketurl: \"def\"\nLicensedfieldlocation: \"ghi\"\n", "Sitename: \"abc\"\nMaxuserperteam: \"1\"\n", @@ -464,39 +460,42 @@ func TestPrintConfigValues(t *testing.T) { } func TestConfigShow(t *testing.T) { + th := Setup() + defer th.TearDown() - // error - assert.Error(t, RunCommand(t, "config", "show", "abc")) + t.Run("error with unknown subcommand", func(t *testing.T) { + assert.Error(t, th.RunCommand(t, "config", "show", "abc")) + }) - // no error - CheckCommand(t, "config", "show") - - // check the output - output := CheckCommand(t, "config", "show") - assert.Contains(t, string(output), "SqlSettings") - assert.Contains(t, string(output), "MessageExportSettings") - assert.Contains(t, string(output), "AnnouncementSettings") + t.Run("successfully dumping config", func(t *testing.T) { + output := th.CheckCommand(t, "config", "show") + assert.Contains(t, string(output), "SqlSettings") + assert.Contains(t, string(output), "MessageExportSettings") + assert.Contains(t, string(output), "AnnouncementSettings") + }) } func TestSetConfig(t *testing.T) { + th := Setup() + defer th.TearDown() + // Error when no argument is given - assert.Error(t, RunCommand(t, "config", "set")) + assert.Error(t, th.RunCommand(t, "config", "set")) // No Error when more than one argument is given - CheckCommand(t, "config", "set", "ThemeSettings.AllowedThemes", "hello", "World") + th.CheckCommand(t, "config", "set", "ThemeSettings.AllowedThemes", "hello", "World") // No Error when two arguments are given - CheckCommand(t, "config", "set", "ThemeSettings.AllowedThemes", "hello") + th.CheckCommand(t, "config", "set", "ThemeSettings.AllowedThemes", "hello") // Error when only one argument is given - assert.Error(t, RunCommand(t, "config", "set", "ThemeSettings.AllowedThemes")) + assert.Error(t, th.RunCommand(t, "config", "set", "ThemeSettings.AllowedThemes")) // Error when config settings not in the config file are given - assert.Error(t, RunCommand(t, "config", "set", "Abc")) + assert.Error(t, th.RunCommand(t, "config", "set", "Abc")) } func TestUpdateMap(t *testing.T) { - // create a config to make changes config := TestNewConfig{ TestNewServiceSettings{ @@ -577,7 +576,6 @@ func TestUpdateMap(t *testing.T) { } func contains(configMap map[string]interface{}, v interface{}, configSettings []string) bool { - res := configMap[configSettings[0]] value := reflect.ValueOf(res) diff --git a/cmd/mattermost/commands/export_test.go b/cmd/mattermost/commands/export_test.go index 89ef45a6a91..a4a1ab1fdeb 100644 --- a/cmd/mattermost/commands/export_test.go +++ b/cmd/mattermost/commands/export_test.go @@ -4,15 +4,11 @@ package commands import ( - "io/ioutil" - "os" - "path/filepath" "testing" "github.com/stretchr/testify/require" "github.com/mattermost/mattermost-server/model" - "github.com/mattermost/mattermost-server/utils" ) // There are no tests that actually run the Message Export job, because it can take a long time to complete depending @@ -20,47 +16,49 @@ import ( // fails fast if invalid flags are supplied func TestMessageExportNotEnabled(t *testing.T) { - configPath := writeTempConfig(t, false) - defer os.RemoveAll(filepath.Dir(configPath)) + th := Setup() + defer th.TearDown() + + config := th.Config() + config.MessageExportSettings.EnableExport = model.NewBool(false) + th.SetConfig(config) // should fail fast because the feature isn't enabled - require.Error(t, RunCommand(t, "--config", configPath, "export", "schedule")) + require.Error(t, th.RunCommand(t, "export", "schedule")) } func TestMessageExportInvalidFormat(t *testing.T) { - configPath := writeTempConfig(t, true) - defer os.RemoveAll(filepath.Dir(configPath)) + th := Setup() + defer th.TearDown() + + config := th.Config() + config.MessageExportSettings.EnableExport = model.NewBool(true) + th.SetConfig(config) // should fail fast because format isn't supported - require.Error(t, RunCommand(t, "--config", configPath, "--format", "not_actiance", "export", "schedule")) + require.Error(t, th.RunCommand(t, "--format", "not_actiance", "export", "schedule")) } func TestMessageExportNegativeExportFrom(t *testing.T) { - configPath := writeTempConfig(t, true) - defer os.RemoveAll(filepath.Dir(configPath)) + th := Setup() + defer th.TearDown() + + config := th.Config() + config.MessageExportSettings.EnableExport = model.NewBool(true) + th.SetConfig(config) // should fail fast because export from must be a valid timestamp - require.Error(t, RunCommand(t, "--config", configPath, "--format", "actiance", "--exportFrom", "-1", "export", "schedule")) + require.Error(t, th.RunCommand(t, "--format", "actiance", "--exportFrom", "-1", "export", "schedule")) } func TestMessageExportNegativeTimeoutSeconds(t *testing.T) { - configPath := writeTempConfig(t, true) - defer os.RemoveAll(filepath.Dir(configPath)) + th := Setup() + defer th.TearDown() + + config := th.Config() + config.MessageExportSettings.EnableExport = model.NewBool(true) + th.SetConfig(config) // should fail fast because timeout seconds must be a positive int - require.Error(t, RunCommand(t, "--config", configPath, "--format", "actiance", "--exportFrom", "0", "--timeoutSeconds", "-1", "export", "schedule")) -} - -func writeTempConfig(t *testing.T, isMessageExportEnabled bool) string { - dir, err := ioutil.TempDir("", "") - require.NoError(t, err) - - utils.TranslationsPreInit() - config, _, _, appErr := utils.LoadConfig("config.json") - require.Nil(t, appErr) - config.MessageExportSettings.EnableExport = model.NewBool(isMessageExportEnabled) - configPath := filepath.Join(dir, "foo.json") - require.NoError(t, ioutil.WriteFile(configPath, []byte(config.ToJson()), 0600)) - - return configPath + require.Error(t, th.RunCommand(t, "--format", "actiance", "--exportFrom", "0", "--timeoutSeconds", "-1", "export", "schedule")) } diff --git a/cmd/mattermost/commands/main_test.go b/cmd/mattermost/commands/main_test.go new file mode 100644 index 00000000000..4f622eef99a --- /dev/null +++ b/cmd/mattermost/commands/main_test.go @@ -0,0 +1,30 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package commands + +import ( + "flag" + "os" + "testing" + + "github.com/mattermost/mattermost-server/api4" + "github.com/mattermost/mattermost-server/testlib" +) + +func TestMain(m *testing.M) { + // Command tests are run by re-invoking the test binary in question, so avoid creating + // another container when we detect same. + flag.Parse() + if filter := flag.Lookup("test.run").Value.String(); filter == "ExecCommand" { + status := m.Run() + os.Exit(status) + return + } + + mainHelper = testlib.NewMainHelper() + defer mainHelper.Close() + api4.UseTestStore(mainHelper.Store) + + mainHelper.Main(m) +} diff --git a/cmd/mattermost/commands/permissions_test.go b/cmd/mattermost/commands/permissions_test.go index 54ccbddb825..3c7ddc49ba2 100644 --- a/cmd/mattermost/commands/permissions_test.go +++ b/cmd/mattermost/commands/permissions_test.go @@ -4,37 +4,26 @@ package commands import ( - "os" - "os/exec" - "strings" "testing" - "github.com/mattermost/mattermost-server/api4" + "github.com/stretchr/testify/assert" + "github.com/mattermost/mattermost-server/utils" ) func TestPermissionsExport_rejectsUnlicensed(t *testing.T) { - permissionsLicenseRequiredTest(t, "export") + th := Setup().InitBasic() + defer th.TearDown() + + actual, _ := th.RunCommandWithOutput(t, "permissions", "export") + assert.Contains(t, actual, utils.T("cli.license.critical")) } func TestPermissionsImport_rejectsUnlicensed(t *testing.T) { - permissionsLicenseRequiredTest(t, "import") -} - -func permissionsLicenseRequiredTest(t *testing.T, subcommand string) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() - path, err := os.Executable() - if err != nil { - t.Fail() - } - args := []string{"-test.run", "ExecCommand", "--", "--disableconfigwatch", "permissions", subcommand} - output, _ := exec.Command(path, args...).CombinedOutput() + actual, _ := th.RunCommandWithOutput(t, "permissions", "import") - actual := string(output) - expected := utils.T("cli.license.critical") - if !strings.Contains(actual, expected) { - t.Errorf("Expected '%v' but got '%v'.", expected, actual) - } + assert.Contains(t, actual, utils.T("cli.license.critical")) } diff --git a/cmd/mattermost/commands/plugin_test.go b/cmd/mattermost/commands/plugin_test.go index 9712ba0e575..e3c8fe18434 100644 --- a/cmd/mattermost/commands/plugin_test.go +++ b/cmd/mattermost/commands/plugin_test.go @@ -5,38 +5,43 @@ import ( "path/filepath" "testing" - "github.com/mattermost/mattermost-server/api4" "github.com/mattermost/mattermost-server/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestPlugin(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + + config := th.Config() + *config.PluginSettings.EnableUploads = true + *config.PluginSettings.Directory = "./test-plugins" + *config.PluginSettings.ClientDirectory = "./test-client-plugins" + th.SetConfig(config) + os.MkdirAll("./test-plugins", os.ModePerm) os.MkdirAll("./test-client-plugins", os.ModePerm) - th := api4.Setup().InitBasic() - defer th.TearDown() - path, _ := utils.FindDir("tests") os.Chdir(filepath.Join("..", "..", "..")) - CheckCommand(t, "--config", filepath.Join(path, "test-config.json"), "plugin", "add", filepath.Join(path, "testplugin.tar.gz")) + th.CheckCommand(t, "plugin", "add", filepath.Join(path, "testplugin.tar.gz")) - CheckCommand(t, "--config", filepath.Join(path, "test-config.json"), "plugin", "enable", "testplugin") - cfg, _, _, err := utils.LoadConfig(filepath.Join(path, "test-config.json")) + th.CheckCommand(t, "plugin", "enable", "testplugin") + cfg, _, _, err := utils.LoadConfig(th.ConfigPath()) require.Nil(t, err) assert.Equal(t, cfg.PluginSettings.PluginStates["testplugin"].Enable, true) - CheckCommand(t, "--config", filepath.Join(path, "test-config.json"), "plugin", "disable", "testplugin") - cfg, _, _, err = utils.LoadConfig(filepath.Join(path, "test-config.json")) + th.CheckCommand(t, "plugin", "disable", "testplugin") + cfg, _, _, err = utils.LoadConfig(th.ConfigPath()) require.Nil(t, err) assert.Equal(t, cfg.PluginSettings.PluginStates["testplugin"].Enable, false) - CheckCommand(t, "--config", filepath.Join(path, "test-config.json"), "plugin", "list") + th.CheckCommand(t, "plugin", "list") - CheckCommand(t, "--config", filepath.Join(path, "test-config.json"), "plugin", "delete", "testplugin") + th.CheckCommand(t, "plugin", "delete", "testplugin") os.Chdir(filepath.Join("cmd", "mattermost", "commands")) } diff --git a/cmd/mattermost/commands/roles_test.go b/cmd/mattermost/commands/roles_test.go index da33a73cc7b..2d768f669fd 100644 --- a/cmd/mattermost/commands/roles_test.go +++ b/cmd/mattermost/commands/roles_test.go @@ -6,15 +6,14 @@ package commands import ( "testing" - "github.com/mattermost/mattermost-server/api4" "github.com/mattermost/mattermost-server/model" ) func TestAssignRole(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() - CheckCommand(t, "roles", "system_admin", th.BasicUser.Email) + th.CheckCommand(t, "roles", "system_admin", th.BasicUser.Email) if result := <-th.App.Srv.Store.User().GetByEmail(th.BasicUser.Email); result.Err != nil { t.Fatal(result.Err) @@ -25,7 +24,7 @@ func TestAssignRole(t *testing.T) { } } - CheckCommand(t, "roles", "member", th.BasicUser.Email) + th.CheckCommand(t, "roles", "member", th.BasicUser.Email) if result := <-th.App.Srv.Store.User().GetByEmail(th.BasicUser.Email); result.Err != nil { t.Fatal(result.Err) diff --git a/cmd/mattermost/commands/sampledata_test.go b/cmd/mattermost/commands/sampledata_test.go index e447fe49245..4ea914e1d60 100644 --- a/cmd/mattermost/commands/sampledata_test.go +++ b/cmd/mattermost/commands/sampledata_test.go @@ -6,20 +6,19 @@ package commands import ( "testing" - "github.com/mattermost/mattermost-server/api4" "github.com/stretchr/testify/require" ) func TestSampledataBadParameters(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() // should fail because you need at least 1 worker - require.Error(t, RunCommand(t, "sampledata", "--workers", "0")) + require.Error(t, th.RunCommand(t, "sampledata", "--workers", "0")) // should fail because you have more team memberships than teams - require.Error(t, RunCommand(t, "sampledata", "--teams", "10", "--teams-memberships", "11")) + require.Error(t, th.RunCommand(t, "sampledata", "--teams", "10", "--teams-memberships", "11")) // should fail because you have more channel memberships than channels per team - require.Error(t, RunCommand(t, "sampledata", "--channels-per-team", "10", "--channel-memberships", "11")) + require.Error(t, th.RunCommand(t, "sampledata", "--channels-per-team", "10", "--channel-memberships", "11")) } diff --git a/cmd/mattermost/commands/team_test.go b/cmd/mattermost/commands/team_test.go index b7bbd090a38..a956e38bedb 100644 --- a/cmd/mattermost/commands/team_test.go +++ b/cmd/mattermost/commands/team_test.go @@ -7,19 +7,18 @@ import ( "strings" "testing" - "github.com/mattermost/mattermost-server/api4" "github.com/mattermost/mattermost-server/model" ) func TestCreateTeam(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() id := model.NewId() name := "name" + id displayName := "Name " + id - CheckCommand(t, "team", "create", "--name", name, "--display_name", displayName) + th.CheckCommand(t, "team", "create", "--name", name, "--display_name", displayName) found := th.SystemAdminClient.Must(th.SystemAdminClient.TeamExists(name, "")).(bool) @@ -29,10 +28,10 @@ func TestCreateTeam(t *testing.T) { } func TestJoinTeam(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() - CheckCommand(t, "team", "add", th.BasicTeam.Name, th.BasicUser.Email) + th.CheckCommand(t, "team", "add", th.BasicTeam.Name, th.BasicUser.Email) profiles := th.SystemAdminClient.Must(th.SystemAdminClient.GetUsersInTeam(th.BasicTeam.Id, 0, 1000, "")).([]*model.User) @@ -51,10 +50,10 @@ func TestJoinTeam(t *testing.T) { } func TestLeaveTeam(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() - CheckCommand(t, "team", "remove", th.BasicTeam.Name, th.BasicUser.Email) + th.CheckCommand(t, "team", "remove", th.BasicTeam.Name, th.BasicUser.Email) profiles := th.Client.Must(th.Client.GetUsersInTeam(th.BasicTeam.Id, 0, 1000, "")).([]*model.User) @@ -80,16 +79,16 @@ func TestLeaveTeam(t *testing.T) { } func TestListTeams(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() id := model.NewId() name := "name" + id displayName := "Name " + id - CheckCommand(t, "team", "create", "--name", name, "--display_name", displayName) + th.CheckCommand(t, "team", "create", "--name", name, "--display_name", displayName) - output := CheckCommand(t, "team", "list", th.BasicTeam.Name, th.BasicUser.Email) + output := th.CheckCommand(t, "team", "list", th.BasicTeam.Name, th.BasicUser.Email) if !strings.Contains(string(output), name) { t.Fatal("should have the created team") @@ -97,18 +96,18 @@ func TestListTeams(t *testing.T) { } func TestListArchivedTeams(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() id := model.NewId() name := "name" + id displayName := "Name " + id - CheckCommand(t, "team", "create", "--name", name, "--display_name", displayName) + th.CheckCommand(t, "team", "create", "--name", name, "--display_name", displayName) - CheckCommand(t, "team", "archive", name) + th.CheckCommand(t, "team", "archive", name) - output := CheckCommand(t, "team", "list", th.BasicTeam.Name, th.BasicUser.Email) + output := th.CheckCommand(t, "team", "list", th.BasicTeam.Name, th.BasicUser.Email) if !strings.Contains(string(output), name+" (archived)") { t.Fatal("should have archived team") @@ -116,16 +115,16 @@ func TestListArchivedTeams(t *testing.T) { } func TestSearchTeamsByName(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() id := model.NewId() name := "name" + id displayName := "Name " + id - CheckCommand(t, "team", "create", "--name", name, "--display_name", displayName) + th.CheckCommand(t, "team", "create", "--name", name, "--display_name", displayName) - output := CheckCommand(t, "team", "search", name) + output := th.CheckCommand(t, "team", "search", name) if !strings.Contains(string(output), name) { t.Fatal("should have the created team") @@ -133,16 +132,16 @@ func TestSearchTeamsByName(t *testing.T) { } func TestSearchTeamsByDisplayName(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() id := model.NewId() name := "name" + id displayName := "Name " + id - CheckCommand(t, "team", "create", "--name", name, "--display_name", displayName) + th.CheckCommand(t, "team", "create", "--name", name, "--display_name", displayName) - output := CheckCommand(t, "team", "search", displayName) + output := th.CheckCommand(t, "team", "search", displayName) if !strings.Contains(string(output), name) { t.Fatal("should have the created team") @@ -150,18 +149,18 @@ func TestSearchTeamsByDisplayName(t *testing.T) { } func TestSearchArchivedTeamsByName(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() id := model.NewId() name := "name" + id displayName := "Name " + id - CheckCommand(t, "team", "create", "--name", name, "--display_name", displayName) + th.CheckCommand(t, "team", "create", "--name", name, "--display_name", displayName) - CheckCommand(t, "team", "archive", name) + th.CheckCommand(t, "team", "archive", name) - output := CheckCommand(t, "team", "search", name) + output := th.CheckCommand(t, "team", "search", name) if !strings.Contains(string(output), "(archived)") { t.Fatal("should have archived team") @@ -169,18 +168,18 @@ func TestSearchArchivedTeamsByName(t *testing.T) { } func TestArchiveTeams(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() id := model.NewId() name := "name" + id displayName := "Name " + id - CheckCommand(t, "team", "create", "--name", name, "--display_name", displayName) + th.CheckCommand(t, "team", "create", "--name", name, "--display_name", displayName) - CheckCommand(t, "team", "archive", name) + th.CheckCommand(t, "team", "archive", name) - output := CheckCommand(t, "team", "list") + output := th.CheckCommand(t, "team", "list") if !strings.Contains(string(output), name+" (archived)") { t.Fatal("should have archived team") diff --git a/cmd/mattermost/commands/user_test.go b/cmd/mattermost/commands/user_test.go index 80582c652b1..632b5efb5b7 100644 --- a/cmd/mattermost/commands/user_test.go +++ b/cmd/mattermost/commands/user_test.go @@ -6,22 +6,21 @@ package commands import ( "testing" - "github.com/mattermost/mattermost-server/api4" "github.com/mattermost/mattermost-server/model" "github.com/stretchr/testify/require" ) func TestCreateUserWithTeam(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() id := model.NewId() email := "success+" + id + "@simulator.amazonses.com" username := "name" + id - CheckCommand(t, "user", "create", "--email", email, "--password", "mypassword1", "--username", username) + th.CheckCommand(t, "user", "create", "--email", email, "--password", "mypassword1", "--username", username) - CheckCommand(t, "team", "add", th.BasicTeam.Id, email) + th.CheckCommand(t, "team", "add", th.BasicTeam.Id, email) profiles := th.SystemAdminClient.Must(th.SystemAdminClient.GetUsersInTeam(th.BasicTeam.Id, 0, 1000, "")).([]*model.User) @@ -40,14 +39,14 @@ func TestCreateUserWithTeam(t *testing.T) { } func TestCreateUserWithoutTeam(t *testing.T) { - th := api4.Setup() + th := Setup() defer th.TearDown() id := model.NewId() email := "success+" + id + "@simulator.amazonses.com" username := "name" + id - CheckCommand(t, "user", "create", "--email", email, "--password", "mypassword1", "--username", username) + th.CheckCommand(t, "user", "create", "--email", email, "--password", "mypassword1", "--username", username) if result := <-th.App.Srv.Store.User().GetByEmail(email); result.Err != nil { t.Fatal(result.Err) @@ -58,10 +57,10 @@ func TestCreateUserWithoutTeam(t *testing.T) { } func TestResetPassword(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() - CheckCommand(t, "user", "password", th.BasicUser.Email, "password2") + th.CheckCommand(t, "user", "password", th.BasicUser.Email, "password2") th.Client.Logout() th.BasicUser.Password = "password2" @@ -69,23 +68,23 @@ func TestResetPassword(t *testing.T) { } func TestMakeUserActiveAndInactive(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() // first inactivate the user - CheckCommand(t, "user", "deactivate", th.BasicUser.Email) + th.CheckCommand(t, "user", "deactivate", th.BasicUser.Email) // activate the inactive user - CheckCommand(t, "user", "activate", th.BasicUser.Email) + th.CheckCommand(t, "user", "activate", th.BasicUser.Email) } func TestChangeUserEmail(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() newEmail := model.NewId() + "@mattermost-test.com" - CheckCommand(t, "user", "email", th.BasicUser.Username, newEmail) + th.CheckCommand(t, "user", "email", th.BasicUser.Username, newEmail) if result := <-th.App.Srv.Store.User().GetByEmail(th.BasicUser.Email); result.Err == nil { t.Fatal("should've updated to the new email") } @@ -99,21 +98,21 @@ func TestChangeUserEmail(t *testing.T) { } // should fail because using an invalid email - require.Error(t, RunCommand(t, "user", "email", th.BasicUser.Username, "wrong$email.com")) + require.Error(t, th.RunCommand(t, "user", "email", th.BasicUser.Username, "wrong$email.com")) // should fail because missing one parameter - require.Error(t, RunCommand(t, "user", "email", th.BasicUser.Username)) + require.Error(t, th.RunCommand(t, "user", "email", th.BasicUser.Username)) // should fail because missing both parameters - require.Error(t, RunCommand(t, "user", "email")) + require.Error(t, th.RunCommand(t, "user", "email")) // should fail because have more than 2 parameters - require.Error(t, RunCommand(t, "user", "email", th.BasicUser.Username, "new@email.com", "extra!")) + require.Error(t, th.RunCommand(t, "user", "email", th.BasicUser.Username, "new@email.com", "extra!")) // should fail because user not found - require.Error(t, RunCommand(t, "user", "email", "invalidUser", newEmail)) + require.Error(t, th.RunCommand(t, "user", "email", "invalidUser", newEmail)) // should fail because email already in use - require.Error(t, RunCommand(t, "user", "email", th.BasicUser.Username, th.BasicUser2.Email)) + require.Error(t, th.RunCommand(t, "user", "email", th.BasicUser.Username, th.BasicUser2.Email)) } diff --git a/cmd/mattermost/commands/version_test.go b/cmd/mattermost/commands/version_test.go index 0ed2c8ec59e..630024d9292 100644 --- a/cmd/mattermost/commands/version_test.go +++ b/cmd/mattermost/commands/version_test.go @@ -8,5 +8,8 @@ import ( ) func TestVersion(t *testing.T) { - CheckCommand(t, "version") + th := Setup() + defer th.TearDown() + + th.CheckCommand(t, "version") } diff --git a/cmd/mattermost/commands/webhook_test.go b/cmd/mattermost/commands/webhook_test.go index 96711596d82..44610ee821f 100644 --- a/cmd/mattermost/commands/webhook_test.go +++ b/cmd/mattermost/commands/webhook_test.go @@ -15,10 +15,18 @@ import ( ) func TestListWebhooks(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() adminClient := th.SystemAdminClient + config := th.Config() + *config.ServiceSettings.EnableCommands = true + config.ServiceSettings.EnableIncomingWebhooks = true + config.ServiceSettings.EnableOutgoingWebhooks = true + config.ServiceSettings.EnablePostUsernameOverride = true + config.ServiceSettings.EnablePostIconOverride = true + th.SetConfig(config) + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnablePostUsernameOverride = true }) @@ -41,7 +49,7 @@ func TestListWebhooks(t *testing.T) { _, resp = adminClient.CreateOutgoingWebhook(outHook) api4.CheckNoError(t, resp) - output := CheckCommand(t, "webhook", "list", th.BasicTeam.Name) + output := th.CheckCommand(t, "webhook", "list", th.BasicTeam.Name) if !strings.Contains(string(output), dispName) { t.Fatal("should have incoming webhooks") @@ -54,9 +62,17 @@ func TestListWebhooks(t *testing.T) { } func TestCreateIncomingWebhook(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() + config := th.Config() + *config.ServiceSettings.EnableCommands = true + config.ServiceSettings.EnableIncomingWebhooks = true + config.ServiceSettings.EnableOutgoingWebhooks = true + config.ServiceSettings.EnablePostUsernameOverride = true + config.ServiceSettings.EnablePostIconOverride = true + th.SetConfig(config) + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnablePostUsernameOverride = true }) @@ -70,16 +86,16 @@ func TestCreateIncomingWebhook(t *testing.T) { th.RemovePermissionFromRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) // should fail because you need to specify valid channel - require.Error(t, RunCommand(t, "webhook", "create-incoming")) - require.Error(t, RunCommand(t, "webhook", "create-incoming", "--channel", th.BasicTeam.Name+":doesnotexist")) + require.Error(t, th.RunCommand(t, "webhook", "create-incoming")) + require.Error(t, th.RunCommand(t, "webhook", "create-incoming", "--channel", th.BasicTeam.Name+":doesnotexist")) // should fail because you need to specify valid user - require.Error(t, RunCommand(t, "webhook", "create-incoming", "--channel", th.BasicChannel.Id)) - require.Error(t, RunCommand(t, "webhook", "create-incoming", "--channel", th.BasicChannel.Id, "--user", "doesnotexist")) + require.Error(t, th.RunCommand(t, "webhook", "create-incoming", "--channel", th.BasicChannel.Id)) + require.Error(t, th.RunCommand(t, "webhook", "create-incoming", "--channel", th.BasicChannel.Id, "--user", "doesnotexist")) description := "myhookinc" displayName := "myhookinc" - CheckCommand(t, "webhook", "create-incoming", "--channel", th.BasicChannel.Id, "--user", th.BasicUser.Email, "--description", description, "--display-name", displayName) + th.CheckCommand(t, "webhook", "create-incoming", "--channel", th.BasicChannel.Id, "--user", th.BasicUser.Email, "--description", description, "--display-name", displayName) webhooks, err := th.App.GetIncomingWebhooksPage(0, 1000) if err != nil { @@ -98,9 +114,17 @@ func TestCreateIncomingWebhook(t *testing.T) { } func TestModifyIncomingWebhook(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() + config := th.Config() + *config.ServiceSettings.EnableCommands = true + config.ServiceSettings.EnableIncomingWebhooks = true + config.ServiceSettings.EnableOutgoingWebhooks = true + config.ServiceSettings.EnablePostUsernameOverride = true + config.ServiceSettings.EnablePostIconOverride = true + th.SetConfig(config) + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnablePostUsernameOverride = true }) @@ -131,9 +155,9 @@ func TestModifyIncomingWebhook(t *testing.T) { }() // should fail because you need to specify valid incoming webhook - require.Error(t, RunCommand(t, "webhook", "modify-incoming", "doesnotexist")) + require.Error(t, th.RunCommand(t, "webhook", "modify-incoming", "doesnotexist")) // should fail because you need to specify valid channel - require.Error(t, RunCommand(t, "webhook", "modify-incoming", oldHook.Id, "--channel", th.BasicTeam.Name+":doesnotexist")) + require.Error(t, th.RunCommand(t, "webhook", "modify-incoming", oldHook.Id, "--channel", th.BasicTeam.Name+":doesnotexist")) modifiedDescription := "myhookincdesc2" modifiedDisplayName := "myhookincname2" @@ -141,7 +165,7 @@ func TestModifyIncomingWebhook(t *testing.T) { modifiedChannelLocked := true modifiedChannelId := th.BasicChannel2.Id - CheckCommand(t, "webhook", "modify-incoming", oldHook.Id, "--channel", modifiedChannelId, "--description", modifiedDescription, "--display-name", modifiedDisplayName, "--icon", modifiedIconUrl, "--lock-to-channel", strconv.FormatBool(modifiedChannelLocked)) + th.CheckCommand(t, "webhook", "modify-incoming", oldHook.Id, "--channel", modifiedChannelId, "--description", modifiedDescription, "--display-name", modifiedDisplayName, "--icon", modifiedIconUrl, "--lock-to-channel", strconv.FormatBool(modifiedChannelLocked)) modifiedHook, err := th.App.GetIncomingWebhook(oldHook.Id) if err != nil { @@ -153,9 +177,17 @@ func TestModifyIncomingWebhook(t *testing.T) { } func TestCreateOutgoingWebhook(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() + config := th.Config() + *config.ServiceSettings.EnableCommands = true + config.ServiceSettings.EnableIncomingWebhooks = true + config.ServiceSettings.EnableOutgoingWebhooks = true + config.ServiceSettings.EnablePostUsernameOverride = true + config.ServiceSettings.EnablePostIconOverride = true + th.SetConfig(config) + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnablePostUsernameOverride = true }) @@ -178,24 +210,24 @@ func TestCreateOutgoingWebhook(t *testing.T) { callbackURL2 := "http://localhost:8000/my-webhook-handler2" // should fail because team is not specified - require.Error(t, RunCommand(t, "webhook", "create-outgoing", "--display-name", displayName, "--trigger-word", triggerWord1, "--trigger-word", triggerWord2, "--url", callbackURL1, "--url", callbackURL2, "--user", user)) + require.Error(t, th.RunCommand(t, "webhook", "create-outgoing", "--display-name", displayName, "--trigger-word", triggerWord1, "--trigger-word", triggerWord2, "--url", callbackURL1, "--url", callbackURL2, "--user", user)) // should fail because user is not specified - require.Error(t, RunCommand(t, "webhook", "create-outgoing", "--team", team, "--display-name", displayName, "--trigger-word", triggerWord1, "--trigger-word", triggerWord2, "--url", callbackURL1, "--url", callbackURL2)) + require.Error(t, th.RunCommand(t, "webhook", "create-outgoing", "--team", team, "--display-name", displayName, "--trigger-word", triggerWord1, "--trigger-word", triggerWord2, "--url", callbackURL1, "--url", callbackURL2)) // should fail because display name is not specified - require.Error(t, RunCommand(t, "webhook", "create-outgoing", "--team", team, "--trigger-word", triggerWord1, "--trigger-word", triggerWord2, "--url", callbackURL1, "--url", callbackURL2, "--user", user)) + require.Error(t, th.RunCommand(t, "webhook", "create-outgoing", "--team", team, "--trigger-word", triggerWord1, "--trigger-word", triggerWord2, "--url", callbackURL1, "--url", callbackURL2, "--user", user)) // should fail because trigger words are not specified - require.Error(t, RunCommand(t, "webhook", "create-outgoing", "--team", team, "--display-name", displayName, "--url", callbackURL1, "--url", callbackURL2, "--user", user)) + require.Error(t, th.RunCommand(t, "webhook", "create-outgoing", "--team", team, "--display-name", displayName, "--url", callbackURL1, "--url", callbackURL2, "--user", user)) // should fail because callback URLs are not specified - require.Error(t, RunCommand(t, "webhook", "create-outgoing", "--team", team, "--display-name", displayName, "--trigger-word", triggerWord1, "--trigger-word", triggerWord2, "--user", user)) + require.Error(t, th.RunCommand(t, "webhook", "create-outgoing", "--team", team, "--display-name", displayName, "--trigger-word", triggerWord1, "--trigger-word", triggerWord2, "--user", user)) // should fail because outgoing webhooks cannot be made for private channels - require.Error(t, RunCommand(t, "webhook", "create-outgoing", "--team", team, "--channel", th.BasicPrivateChannel.Id, "--display-name", displayName, "--trigger-word", triggerWord1, "--trigger-word", triggerWord2, "--url", callbackURL1, "--url", callbackURL2, "--user", user)) + require.Error(t, th.RunCommand(t, "webhook", "create-outgoing", "--team", team, "--channel", th.BasicPrivateChannel.Id, "--display-name", displayName, "--trigger-word", triggerWord1, "--trigger-word", triggerWord2, "--url", callbackURL1, "--url", callbackURL2, "--user", user)) - CheckCommand(t, "webhook", "create-outgoing", "--team", team, "--channel", th.BasicChannel.Id, "--display-name", displayName, "--trigger-word", triggerWord1, "--trigger-word", triggerWord2, "--url", callbackURL1, "--url", callbackURL2, "--user", user) + th.CheckCommand(t, "webhook", "create-outgoing", "--team", team, "--channel", th.BasicChannel.Id, "--display-name", displayName, "--trigger-word", triggerWord1, "--trigger-word", triggerWord2, "--url", callbackURL1, "--url", callbackURL2, "--user", user) webhooks, err := th.App.GetOutgoingWebhooksPage(0, 1000) if err != nil { @@ -214,10 +246,18 @@ func TestCreateOutgoingWebhook(t *testing.T) { } func TestDeleteWebhooks(t *testing.T) { - th := api4.Setup().InitBasic() + th := Setup().InitBasic() defer th.TearDown() adminClient := th.SystemAdminClient + config := th.Config() + *config.ServiceSettings.EnableCommands = true + config.ServiceSettings.EnableIncomingWebhooks = true + config.ServiceSettings.EnableOutgoingWebhooks = true + config.ServiceSettings.EnablePostUsernameOverride = true + config.ServiceSettings.EnablePostIconOverride = true + th.SetConfig(config) + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnablePostUsernameOverride = true }) @@ -240,7 +280,7 @@ func TestDeleteWebhooks(t *testing.T) { outgoingHook, resp := adminClient.CreateOutgoingWebhook(outHookStruct) api4.CheckNoError(t, resp) - hooksBeforeDeletion := CheckCommand(t, "webhook", "list", th.BasicTeam.Name) + hooksBeforeDeletion := th.CheckCommand(t, "webhook", "list", th.BasicTeam.Name) if !strings.Contains(string(hooksBeforeDeletion), dispName) { t.Fatal("Should have incoming webhooks") @@ -250,10 +290,10 @@ func TestDeleteWebhooks(t *testing.T) { t.Fatal("Should have outgoing webhooks") } - CheckCommand(t, "webhook", "delete", incomingHook.Id) - CheckCommand(t, "webhook", "delete", outgoingHook.Id) + th.CheckCommand(t, "webhook", "delete", incomingHook.Id) + th.CheckCommand(t, "webhook", "delete", outgoingHook.Id) - hooksAfterDeletion := CheckCommand(t, "webhook", "list", th.BasicTeam.Name) + hooksAfterDeletion := th.CheckCommand(t, "webhook", "list", th.BasicTeam.Name) if strings.Contains(string(hooksAfterDeletion), dispName) { t.Fatal("Should not have incoming webhooks") diff --git a/migrations/migrationstestlib.go b/migrations/helper_test.go similarity index 66% rename from migrations/migrationstestlib.go rename to migrations/helper_test.go index e44067a357d..586de27cf22 100644 --- a/migrations/migrationstestlib.go +++ b/migrations/helper_test.go @@ -10,12 +10,8 @@ import ( "time" "github.com/mattermost/mattermost-server/app" - "github.com/mattermost/mattermost-server/einterfaces" "github.com/mattermost/mattermost-server/mlog" "github.com/mattermost/mattermost-server/model" - "github.com/mattermost/mattermost-server/store" - "github.com/mattermost/mattermost-server/store/sqlstore" - "github.com/mattermost/mattermost-server/store/storetest" "github.com/mattermost/mattermost-server/utils" ) @@ -34,37 +30,8 @@ type TestHelper struct { tempWorkspace string } -type persistentTestStore struct { - store.Store -} - -func (*persistentTestStore) Close() {} - -var testStoreContainer *storetest.RunningContainer -var testStore *persistentTestStore -var testStoreSqlSupplier *sqlstore.SqlSupplier -var testClusterInterface *FakeClusterInterface - -// UseTestStore sets the container and corresponding settings to use for tests. Once the tests are -// complete (e.g. at the end of your TestMain implementation), you should call StopTestStore. -func UseTestStore(container *storetest.RunningContainer, settings *model.SqlSettings) { - testClusterInterface = &FakeClusterInterface{} - testStoreContainer = container - testStoreSqlSupplier = sqlstore.NewSqlSupplier(*settings, nil) - testStore = &persistentTestStore{store.NewLayeredStore(testStoreSqlSupplier, nil, testClusterInterface)} -} - -func StopTestStore() { - if testStoreContainer != nil { - testStoreContainer.Stop() - testStoreContainer = nil - } -} - func setupTestHelper(enterprise bool) *TestHelper { - if testStore != nil { - testStore.DropAllTables() - } + mainHelper.Store.DropAllTables() permConfig, err := os.Open(utils.FindConfigFile("config.json")) if err != nil { @@ -82,9 +49,7 @@ func setupTestHelper(enterprise bool) *TestHelper { } options := []app.Option{app.ConfigFile(tempConfig.Name()), app.DisableConfigWatch} - if testStore != nil { - options = append(options, app.StoreOverride(testStore)) - } + options = append(options, app.StoreOverride(mainHelper.Store)) s, err := app.NewServer(options...) if err != nil { @@ -100,9 +65,8 @@ func setupTestHelper(enterprise bool) *TestHelper { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.MaxUsersPerTeam = 50 }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.RateLimitSettings.Enable = false }) prevListenAddress := *th.App.Config().ServiceSettings.ListenAddress - if testStore != nil { - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" }) - } + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" }) + serverErr := th.App.StartServer() if serverErr != nil { panic(serverErr) @@ -295,7 +259,6 @@ func (me *TestHelper) TearDown() { me.Server.Shutdown() os.Remove(me.tempConfigPath) if err := recover(); err != nil { - StopTestStore() panic(err) } if me.tempWorkspace != "" { @@ -304,13 +267,13 @@ func (me *TestHelper) TearDown() { } func (me *TestHelper) ResetRoleMigration() { - if _, err := testStoreSqlSupplier.GetMaster().Exec("DELETE from Roles"); err != nil { + if _, err := mainHelper.SqlSupplier.GetMaster().Exec("DELETE from Roles"); err != nil { panic(err) } - testClusterInterface.sendClearRoleCacheMessage() + mainHelper.ClusterInterface.SendClearRoleCacheMessage() - if _, err := testStoreSqlSupplier.GetMaster().Exec("DELETE from Systems where Name = :Name", map[string]interface{}{"Name": app.ADVANCED_PERMISSIONS_MIGRATION_KEY}); err != nil { + if _, err := mainHelper.SqlSupplier.GetMaster().Exec("DELETE from Systems where Name = :Name", map[string]interface{}{"Name": app.ADVANCED_PERMISSIONS_MIGRATION_KEY}); err != nil { panic(err) } } @@ -330,36 +293,3 @@ func (me *TestHelper) DeleteAllJobsByTypeAndMigrationKey(jobType string, migrati } } } - -type FakeClusterInterface struct { - clusterMessageHandler einterfaces.ClusterMessageHandler -} - -func (me *FakeClusterInterface) StartInterNodeCommunication() {} -func (me *FakeClusterInterface) StopInterNodeCommunication() {} -func (me *FakeClusterInterface) RegisterClusterMessageHandler(event string, crm einterfaces.ClusterMessageHandler) { - me.clusterMessageHandler = crm -} -func (me *FakeClusterInterface) GetClusterId() string { return "" } -func (me *FakeClusterInterface) IsLeader() bool { return false } -func (me *FakeClusterInterface) GetMyClusterInfo() *model.ClusterInfo { return nil } -func (me *FakeClusterInterface) GetClusterInfos() []*model.ClusterInfo { return nil } -func (me *FakeClusterInterface) SendClusterMessage(cluster *model.ClusterMessage) {} -func (me *FakeClusterInterface) NotifyMsg(buf []byte) {} -func (me *FakeClusterInterface) GetClusterStats() ([]*model.ClusterStats, *model.AppError) { - return nil, nil -} -func (me *FakeClusterInterface) GetLogs(page, perPage int) ([]string, *model.AppError) { - return []string{}, nil -} -func (me *FakeClusterInterface) ConfigChanged(previousConfig *model.Config, newConfig *model.Config, sendToOtherServer bool) *model.AppError { - return nil -} -func (me *FakeClusterInterface) sendClearRoleCacheMessage() { - me.clusterMessageHandler(&model.ClusterMessage{ - Event: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES, - }) -} -func (me *FakeClusterInterface) GetPluginStatuses() (model.PluginStatuses, *model.AppError) { - return nil, nil -} diff --git a/migrations/main_test.go b/migrations/main_test.go new file mode 100644 index 00000000000..89d1c31072f --- /dev/null +++ b/migrations/main_test.go @@ -0,0 +1,19 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package migrations + +import ( + "testing" + + "github.com/mattermost/mattermost-server/testlib" +) + +var mainHelper *testlib.MainHelper + +func TestMain(m *testing.M) { + mainHelper = testlib.NewMainHelper() + defer mainHelper.Close() + + mainHelper.Main(m) +} diff --git a/migrations/migrations_test.go b/migrations/migrations_test.go index 30831943064..aabfd75a8b5 100644 --- a/migrations/migrations_test.go +++ b/migrations/migrations_test.go @@ -4,56 +4,13 @@ package migrations import ( - "flag" - "os" "testing" "github.com/stretchr/testify/assert" - "github.com/mattermost/mattermost-server/mlog" "github.com/mattermost/mattermost-server/model" - "github.com/mattermost/mattermost-server/store/storetest" - "github.com/mattermost/mattermost-server/utils" ) -func TestMain(m *testing.M) { - flag.Parse() - - // Setup a global logger to catch tests logging outside of app context - // The global logger will be stomped by apps initalizing but that's fine for testing. Ideally this won't happen. - mlog.InitGlobalLogger(mlog.NewLogger(&mlog.LoggerConfiguration{ - EnableConsole: true, - ConsoleJson: true, - ConsoleLevel: "error", - EnableFile: false, - })) - - utils.TranslationsPreInit() - - // In the case where a dev just wants to run a single test, it's faster to just use the default - // store. - if filter := flag.Lookup("test.run").Value.String(); filter != "" && filter != "." { - mlog.Info("-test.run used, not creating temporary containers") - os.Exit(m.Run()) - } - - status := 0 - - container, settings, err := storetest.NewMySQLContainer() - if err != nil { - panic(err) - } - - UseTestStore(container, settings) - - defer func() { - StopTestStore() - os.Exit(status) - }() - - status = m.Run() -} - func TestGetMigrationState(t *testing.T) { th := Setup() defer th.TearDown() diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 00000000000..7d4d620d115 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -o pipefail + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" + +GO=$1 +GOFLAGS=$2 +PACKAGES=$3 +TESTS=$4 +TESTFLAGS=$5 + +PACKAGES_COMMA=$(echo $PACKAGES | tr ' ' ',') + +echo "Packages to test: $PACKAGES" +find . -name 'cprofile*.out' -exec sh -c 'rm "{}"' \; +find . -type d -name data -not -path './vendor/*' | xargs rm -rf + +$GO test $GOFLAGS -run=$TESTS $TESTFLAGS -p 1 -v -timeout=2000s -covermode=count -coverpkg=$PACKAGES_COMMA -exec $DIR/test-xprog.sh $PACKAGES 2>&1 | tee output +EXIT_STATUS=$? + +cat output | $GOPATH/bin/go-junit-report > report.xml +rm output +find . -name 'cprofile*.out' -exec sh -c 'tail -n +2 {} >> cover.out ; rm "{}"' \; +rm -f config/*.crt +rm -f config/*.key + +exit $EXIT_STATUS diff --git a/testlib/cluster.go b/testlib/cluster.go new file mode 100644 index 00000000000..532672baad0 --- /dev/null +++ b/testlib/cluster.go @@ -0,0 +1,55 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package testlib + +import ( + "github.com/mattermost/mattermost-server/einterfaces" + "github.com/mattermost/mattermost-server/model" +) + +type FakeClusterInterface struct { + clusterMessageHandler einterfaces.ClusterMessageHandler +} + +func (c *FakeClusterInterface) StartInterNodeCommunication() {} + +func (c *FakeClusterInterface) StopInterNodeCommunication() {} + +func (c *FakeClusterInterface) RegisterClusterMessageHandler(event string, crm einterfaces.ClusterMessageHandler) { + c.clusterMessageHandler = crm +} + +func (c *FakeClusterInterface) GetClusterId() string { return "" } + +func (c *FakeClusterInterface) IsLeader() bool { return false } + +func (c *FakeClusterInterface) GetMyClusterInfo() *model.ClusterInfo { return nil } + +func (c *FakeClusterInterface) GetClusterInfos() []*model.ClusterInfo { return nil } + +func (c *FakeClusterInterface) SendClusterMessage(cluster *model.ClusterMessage) {} + +func (c *FakeClusterInterface) NotifyMsg(buf []byte) {} + +func (c *FakeClusterInterface) GetClusterStats() ([]*model.ClusterStats, *model.AppError) { + return nil, nil +} + +func (c *FakeClusterInterface) GetLogs(page, perPage int) ([]string, *model.AppError) { + return []string{}, nil +} + +func (c *FakeClusterInterface) ConfigChanged(previousConfig *model.Config, newConfig *model.Config, sendToOtherServer bool) *model.AppError { + return nil +} + +func (c *FakeClusterInterface) SendClearRoleCacheMessage() { + c.clusterMessageHandler(&model.ClusterMessage{ + Event: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES, + }) +} + +func (c *FakeClusterInterface) GetPluginStatuses() (model.PluginStatuses, *model.AppError) { + return nil, nil +} diff --git a/testlib/doc.go b/testlib/doc.go new file mode 100644 index 00000000000..77897e96c15 --- /dev/null +++ b/testlib/doc.go @@ -0,0 +1,5 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +// testlib exposes helper methods for running unit tests against a containerized test store. +package testlib diff --git a/testlib/helper.go b/testlib/helper.go new file mode 100644 index 00000000000..bcc19f60a7f --- /dev/null +++ b/testlib/helper.go @@ -0,0 +1,70 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package testlib + +import ( + "os" + "testing" + + "github.com/mattermost/mattermost-server/mlog" + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" + "github.com/mattermost/mattermost-server/store/sqlstore" + "github.com/mattermost/mattermost-server/store/storetest" + "github.com/mattermost/mattermost-server/utils" +) + +type MainHelper struct { + Settings *model.SqlSettings + Store store.Store + SqlSupplier *sqlstore.SqlSupplier + ClusterInterface *FakeClusterInterface + + container *storetest.RunningContainer + status int +} + +func NewMainHelper() *MainHelper { + // Setup a global logger to catch tests logging outside of app context + // The global logger will be stomped by apps initalizing but that's fine for testing. + // Ideally this won't happen. + mlog.InitGlobalLogger(mlog.NewLogger(&mlog.LoggerConfiguration{ + EnableConsole: true, + ConsoleJson: true, + ConsoleLevel: "error", + EnableFile: false, + })) + + utils.TranslationsPreInit() + + container, settings, err := storetest.NewMySQLContainer() + if err != nil { + panic("failed to start mysql container: " + err.Error()) + } + + testClusterInterface := &FakeClusterInterface{} + testStoreSqlSupplier := sqlstore.NewSqlSupplier(*settings, nil) + testStore := &TestStore{store.NewLayeredStore(testStoreSqlSupplier, nil, testClusterInterface)} + + return &MainHelper{ + Settings: settings, + Store: testStore, + SqlSupplier: testStoreSqlSupplier, + ClusterInterface: testClusterInterface, + container: container, + } +} + +func (h *MainHelper) Main(m *testing.M) { + h.status = m.Run() +} + +func (h *MainHelper) Close() error { + h.container.Stop() + h.container = nil + + os.Exit(h.status) + + return nil +} diff --git a/testlib/store.go b/testlib/store.go new file mode 100644 index 00000000000..5d20d3754d7 --- /dev/null +++ b/testlib/store.go @@ -0,0 +1,15 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package testlib + +import ( + "github.com/mattermost/mattermost-server/store" +) + +type TestStore struct { + store.Store +} + +func (*TestStore) Close() { +} diff --git a/tests/test-config.json b/tests/test-config.json deleted file mode 100644 index 69b784ff5ad..00000000000 --- a/tests/test-config.json +++ /dev/null @@ -1,401 +0,0 @@ -{ - "ServiceSettings": { - "SiteURL": "", - "WebsocketURL": "", - "LicenseFileLocation": "", - "ListenAddress": ":8065", - "ConnectionSecurity": "", - "TLSCertFile": "", - "TLSKeyFile": "", - "UseLetsEncrypt": false, - "LetsEncryptCertificateCacheFile": "./config/letsencrypt.cache", - "Forward80To443": false, - "ReadTimeout": 300, - "WriteTimeout": 300, - "MaximumLoginAttempts": 10, - "GoroutineHealthThreshold": -1, - "GoogleDeveloperKey": "", - "EnableOAuthServiceProvider": false, - "EnableIncomingWebhooks": true, - "EnableOutgoingWebhooks": true, - "EnableCommands": true, - "EnableOnlyAdminIntegrations": true, - "EnablePostUsernameOverride": false, - "EnablePostIconOverride": false, - "EnableLinkPreviews": false, - "EnableTesting": false, - "EnableDeveloper": false, - "EnableSecurityFixAlert": true, - "EnableInsecureOutgoingConnections": false, - "AllowedUntrustedInternalConnections": "", - "EnableMultifactorAuthentication": false, - "EnforceMultifactorAuthentication": false, - "EnableUserAccessTokens": false, - "AllowCorsFrom": "", - "AllowCookiesForSubdomains": false, - "SessionLengthWebInDays": 30, - "SessionLengthMobileInDays": 30, - "SessionLengthSSOInDays": 30, - "SessionCacheInMinutes": 10, - "SessionIdleTimeoutInMinutes": 0, - "WebsocketSecurePort": 443, - "WebsocketPort": 80, - "WebserverMode": "gzip", - "EnableCustomEmoji": false, - "EnableEmojiPicker": true, - "RestrictCustomEmojiCreation": "all", - "RestrictPostDelete": "all", - "AllowEditPost": "always", - "PostEditTimeLimit": -1, - "TimeBetweenUserTypingUpdatesMilliseconds": 5000, - "EnablePostSearch": true, - "EnableUserTypingMessages": true, - "EnableChannelViewedMessages": true, - "EnableUserStatuses": true, - "ExperimentalEnableAuthenticationTransfer": true, - "ClusterLogTimeoutMilliseconds": 2000, - "CloseUnusedDirectMessages": false, - "EnablePreviewFeatures": true, - "EnableTutorial": true, - "ExperimentalEnableDefaultChannelLeaveJoinMessages": true, - "ExperimentalGroupUnreadChannels": "disabled", - "ImageProxyType": "", - "ImageProxyURL": "", - "ImageProxyOptions": "", - "EnableAPITeamDeletion": false, - "ExperimentalEnableHardenedMode": false - }, - "TeamSettings": { - "SiteName": "Mattermost", - "MaxUsersPerTeam": 50, - "EnableTeamCreation": true, - "EnableUserCreation": true, - "EnableOpenServer": false, - "EnableUserDeactivation": false, - "RestrictCreationToDomains": "", - "EnableCustomBrand": false, - "CustomBrandText": "", - "CustomDescriptionText": "", - "RestrictDirectMessage": "any", - "RestrictTeamInvite": "all", - "RestrictPublicChannelManagement": "all", - "RestrictPrivateChannelManagement": "all", - "RestrictPublicChannelCreation": "all", - "RestrictPrivateChannelCreation": "all", - "RestrictPublicChannelDeletion": "all", - "RestrictPrivateChannelDeletion": "all", - "RestrictPrivateChannelManageMembers": "all", - "EnableXToLeaveChannelsFromLHS": false, - "UserStatusAwayTimeout": 300, - "MaxChannelsPerTeam": 2000, - "MaxNotificationsPerChannel": 1000, - "EnableConfirmNotificationsToChannel": true, - "TeammateNameDisplay": "username", - "ExperimentalEnableAutomaticReplies": false, - "ExperimentalHideTownSquareinLHS": false, - "ExperimentalTownSquareIsReadOnly": false, - "ExperimentalPrimaryTeam": "" - }, - "ClientRequirements": { - "AndroidLatestVersion": "", - "AndroidMinVersion": "", - "DesktopLatestVersion": "", - "DesktopMinVersion": "", - "IosLatestVersion": "", - "IosMinVersion": "" - }, - "SqlSettings": { - "DriverName": "mysql", - "DataSource": "mmuser:mostest@tcp(dockerhost:3306)/mattermost_test?charset=utf8mb4,utf8\u0026readTimeout=30s\u0026writeTimeout=30s", - "DataSourceReplicas": [], - "DataSourceSearchReplicas": [], - "MaxIdleConns": 20, - "MaxOpenConns": 300, - "Trace": false, - "AtRestEncryptKey": "jdh9iergmse3w9mt53snasugmmi9r6it", - "QueryTimeout": 30 - }, - "LogSettings": { - "EnableConsole": true, - "ConsoleLevel": "DEBUG", - "ConsoleJson": true, - "EnableFile": true, - "FileLevel": "INFO", - "FileJson": true, - "FileLocation": "", - "EnableWebhookDebugging": true, - "EnableDiagnostics": true - }, - "PasswordSettings": { - "MinimumLength": 5, - "Lowercase": false, - "Number": false, - "Uppercase": false, - "Symbol": false - }, - "FileSettings": { - "EnableFileAttachments": true, - "EnableMobileUpload": true, - "EnableMobileDownload": true, - "MaxFileSize": 52428800, - "DriverName": "local", - "Directory": "./data/", - "EnablePublicLink": false, - "PublicLinkSalt": "3xh7ztscuezjp1jkdjybtejrtw59xjt1", - "InitialFont": "luximbi.ttf", - "AmazonS3AccessKeyId": "", - "AmazonS3SecretAccessKey": "", - "AmazonS3Bucket": "", - "AmazonS3Region": "", - "AmazonS3Endpoint": "s3.amazonaws.com", - "AmazonS3SSL": true, - "AmazonS3SignV2": false, - "AmazonS3SSE": false, - "AmazonS3Trace": false - }, - "EmailSettings": { - "EnableSignUpWithEmail": true, - "EnableSignInWithEmail": true, - "EnableSignInWithUsername": true, - "SendEmailNotifications": true, - "UseChannelInEmailNotifications": false, - "RequireEmailVerification": false, - "FeedbackName": "", - "FeedbackEmail": "test@example.com", - "FeedbackOrganization": "", - "EnableSMTPAuth": false, - "SMTPUsername": "", - "SMTPPassword": "", - "SMTPServer": "dockerhost", - "SMTPPort": "2500", - "ConnectionSecurity": "", - "InviteSalt": "n3mceqsek4j5ichs5hw9sudwx3cfbtqa", - "SendPushNotifications": false, - "PushNotificationServer": "", - "PushNotificationContents": "generic", - "EnableEmailBatching": false, - "EmailBatchingBufferSize": 256, - "EmailBatchingInterval": 30, - "EnablePreviewModeBanner": true, - "SkipServerCertificateVerification": false, - "EmailNotificationContentsType": "full", - "LoginButtonColor": "", - "LoginButtonBorderColor": "", - "LoginButtonTextColor": "" - }, - "RateLimitSettings": { - "Enable": false, - "PerSec": 10, - "MaxBurst": 100, - "MemoryStoreSize": 10000, - "VaryByRemoteAddr": true, - "VaryByUser": false, - "VaryByHeader": "" - }, - "PrivacySettings": { - "ShowEmailAddress": true, - "ShowFullName": true - }, - "SupportSettings": { - "TermsOfServiceLink": "https://about.mattermost.com/default-terms/", - "PrivacyPolicyLink": "https://about.mattermost.com/default-privacy-policy/", - "AboutLink": "https://about.mattermost.com/default-about/", - "HelpLink": "https://about.mattermost.com/default-help/", - "ReportAProblemLink": "https://about.mattermost.com/default-report-a-problem/", - "SupportEmail": "feedback@mattermost.com" - }, - "AnnouncementSettings": { - "EnableBanner": false, - "BannerText": "", - "BannerColor": "#f2a93b", - "BannerTextColor": "#333333", - "AllowBannerDismissal": true - }, - "ThemeSettings": { - "EnableThemeSelection": true, - "DefaultTheme": "default", - "AllowCustomThemes": true, - "AllowedThemes": [] - }, - "GitLabSettings": { - "Enable": false, - "Secret": "", - "Id": "", - "Scope": "", - "AuthEndpoint": "", - "TokenEndpoint": "", - "UserApiEndpoint": "" - }, - "GoogleSettings": { - "Enable": false, - "Secret": "", - "Id": "", - "Scope": "profile email", - "AuthEndpoint": "https://accounts.google.com/o/oauth2/v2/auth", - "TokenEndpoint": "https://www.googleapis.com/oauth2/v4/token", - "UserApiEndpoint": "https://www.googleapis.com/plus/v1/people/me" - }, - "Office365Settings": { - "Enable": false, - "Secret": "", - "Id": "", - "Scope": "User.Read", - "AuthEndpoint": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize", - "TokenEndpoint": "https://login.microsoftonline.com/common/oauth2/v2.0/token", - "UserApiEndpoint": "https://graph.microsoft.com/v1.0/me" - }, - "LdapSettings": { - "Enable": false, - "EnableSync": false, - "LdapServer": "", - "LdapPort": 389, - "ConnectionSecurity": "", - "BaseDN": "", - "BindUsername": "", - "BindPassword": "", - "UserFilter": "", - "FirstNameAttribute": "", - "LastNameAttribute": "", - "EmailAttribute": "", - "UsernameAttribute": "", - "NicknameAttribute": "", - "IdAttribute": "", - "PositionAttribute": "", - "LoginIdAttribute": "", - "SyncIntervalMinutes": 60, - "SkipCertificateVerification": false, - "QueryTimeout": 60, - "MaxPageSize": 0, - "LoginFieldName": "", - "LoginButtonColor": "", - "LoginButtonBorderColor": "", - "LoginButtonTextColor": "" - }, - "ComplianceSettings": { - "Enable": false, - "Directory": "./data/", - "EnableDaily": false - }, - "LocalizationSettings": { - "DefaultServerLocale": "en", - "DefaultClientLocale": "en", - "AvailableLocales": "" - }, - "SamlSettings": { - "Enable": false, - "EnableSyncWithLdap": false, - "Verify": true, - "Encrypt": true, - "IdpUrl": "", - "IdpDescriptorUrl": "", - "AssertionConsumerServiceURL": "", - "ScopingIDPProviderId": "", - "ScopingIDPName": "", - "IdpCertificateFile": "", - "PublicCertificateFile": "", - "PrivateKeyFile": "", - "FirstNameAttribute": "", - "LastNameAttribute": "", - "EmailAttribute": "", - "UsernameAttribute": "", - "NicknameAttribute": "", - "LocaleAttribute": "", - "PositionAttribute": "", - "LoginButtonText": "With SAML", - "LoginButtonColor": "", - "LoginButtonBorderColor": "", - "LoginButtonTextColor": "" - }, - "NativeAppSettings": { - "AppDownloadLink": "https://about.mattermost.com/downloads/", - "AndroidAppDownloadLink": "https://about.mattermost.com/mattermost-android-app/", - "IosAppDownloadLink": "https://about.mattermost.com/mattermost-ios-app/" - }, - "ClusterSettings": { - "Enable": false, - "ClusterName": "", - "OverrideHostname": "", - "UseIpAddress": true, - "UseExperimentalGossip": false, - "ReadOnlyConfig": true, - "GossipPort": 8074, - "StreamingPort": 8075, - "MaxIdleConns": 100, - "MaxIdleConnsPerHost": 128, - "IdleConnTimeoutMilliseconds": 90000 - }, - "MetricsSettings": { - "Enable": false, - "BlockProfileRate": 0, - "ListenAddress": ":8067" - }, - "ExperimentalSettings": { - "ClientSideCertEnable": false, - "ClientSideCertCheck": "secondary" - }, - "AnalyticsSettings": { - "MaxUsersForStatistics": 2500 - }, - "ElasticsearchSettings": { - "ConnectionUrl": "http://dockerhost:9200", - "Username": "elastic", - "Password": "changeme", - "EnableIndexing": false, - "EnableSearching": false, - "Sniff": true, - "PostIndexReplicas": 1, - "PostIndexShards": 1, - "AggregatePostsAfterDays": 365, - "PostsAggregatorJobStartTime": "03:00", - "IndexPrefix": "", - "LiveIndexingBatchSize": 1, - "BulkIndexingTimeWindowSeconds": 3600, - "RequestTimeoutSeconds": 30 - }, - "DataRetentionSettings": { - "EnableMessageDeletion": false, - "EnableFileDeletion": false, - "MessageRetentionDays": 365, - "FileRetentionDays": 365, - "DeletionJobStartTime": "02:00" - }, - "MessageExportSettings": { - "EnableExport": false, - "ExportFormat": "actiance", - "DailyRunTime": "01:00", - "ExportFromTimestamp": 0, - "BatchSize": 10000, - "GlobalRelaySettings": { - "CustomerType": "A9", - "SmtpUsername": "", - "SmtpPassword": "", - "EmailAddress": "" - } - }, - "JobSettings": { - "RunJobs": true, - "RunScheduler": true - }, - "PluginSettings": { - "Enable": true, - "EnableUploads": true, - "Directory": "./test-plugins", - "ClientDirectory": "./test-client-plugins", - "Plugins": {}, - "PluginStates": { - "jira": { - "Enable": true - }, - "testplugin": { - "Enable": false - } - } - }, - "DisplaySettings": { - "CustomUrlSchemes": [], - "ExperimentalTimezone": false - }, - "TimezoneSettings": { - "SupportedTimezonesPath": "timezones.json" - } -} diff --git a/web/handlers_test.go b/web/handlers_test.go index 817352a112f..2cd3b195951 100644 --- a/web/handlers_test.go +++ b/web/handlers_test.go @@ -18,7 +18,7 @@ func handlerForHTTPErrors(c *Context, w http.ResponseWriter, r *http.Request) { } func TestHandlerServeHTTPErrors(t *testing.T) { - s, err := app.NewServer(app.StoreOverride(testStore), app.DisableConfigWatch) + s, err := app.NewServer(app.StoreOverride(mainHelper.Store), app.DisableConfigWatch) defer s.Shutdown() web := New(s, s.AppOptions, s.Router) @@ -61,7 +61,7 @@ func handlerForHTTPSecureTransport(c *Context, w http.ResponseWriter, r *http.Re } func TestHandlerServeHTTPSecureTransport(t *testing.T) { - s, err := app.NewServer(app.StoreOverride(testStore), app.DisableConfigWatch) + s, err := app.NewServer(app.StoreOverride(mainHelper.Store), app.DisableConfigWatch) defer s.Shutdown() a := s.FakeApp() diff --git a/web/main_test.go b/web/main_test.go new file mode 100644 index 00000000000..49b9f82a9a0 --- /dev/null +++ b/web/main_test.go @@ -0,0 +1,19 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package web + +import ( + "testing" + + "github.com/mattermost/mattermost-server/testlib" +) + +var mainHelper *testlib.MainHelper + +func TestMain(m *testing.M) { + mainHelper = testlib.NewMainHelper() + defer mainHelper.Close() + + mainHelper.Main(m) +} diff --git a/web/web_test.go b/web/web_test.go index 49b0d18acfb..9b422982826 100644 --- a/web/web_test.go +++ b/web/web_test.go @@ -5,37 +5,15 @@ package web import ( "fmt" - "os" "testing" "github.com/mattermost/mattermost-server/app" - "github.com/mattermost/mattermost-server/mlog" "github.com/mattermost/mattermost-server/model" - "github.com/mattermost/mattermost-server/store" - "github.com/mattermost/mattermost-server/store/sqlstore" - "github.com/mattermost/mattermost-server/store/storetest" - "github.com/mattermost/mattermost-server/utils" ) var ApiClient *model.Client4 var URL string -type persistentTestStore struct { - store.Store -} - -func (*persistentTestStore) Close() {} - -var testStoreContainer *storetest.RunningContainer -var testStore *persistentTestStore - -func StopTestStore() { - if testStoreContainer != nil { - testStoreContainer.Stop() - testStoreContainer = nil - } -} - type TestHelper struct { App *app.App Server *app.Server @@ -48,7 +26,9 @@ type TestHelper struct { } func Setup() *TestHelper { - s, err := app.NewServer(app.StoreOverride(testStore), app.DisableConfigWatch) + mainHelper.Store.DropAllTables() + + s, err := app.NewServer(app.StoreOverride(mainHelper.Store), app.DisableConfigWatch) if err != nil { panic(err) } @@ -103,7 +83,6 @@ func (th *TestHelper) InitBasic() *TestHelper { func (th *TestHelper) TearDown() { th.Server.Shutdown() if err := recover(); err != nil { - StopTestStore() panic(err) } } @@ -125,37 +104,6 @@ func TestStatic(t *testing.T) { } */ -func TestMain(m *testing.M) { - // Setup a global logger to catch tests logging outside of app context - // The global logger will be stomped by apps initalizing but that's fine for testing. Ideally this won't happen. - mlog.InitGlobalLogger(mlog.NewLogger(&mlog.LoggerConfiguration{ - EnableConsole: true, - ConsoleJson: true, - ConsoleLevel: "error", - EnableFile: false, - })) - - utils.TranslationsPreInit() - - status := 0 - - container, settings, err := storetest.NewPostgreSQLContainer() - if err != nil { - panic(err) - } - - testStoreContainer = container - testStore = &persistentTestStore{store.NewLayeredStore(sqlstore.NewSqlSupplier(*settings, nil), nil, nil)} - - defer func() { - StopTestStore() - os.Exit(status) - }() - - status = m.Run() - -} - func TestCheckClientCompatability(t *testing.T) { //Browser Name, UA String, expected result (if the browser should fail the test false and if it should pass the true) type uaTest struct {