From 34285d8cca93fc0f473636e78680fade03f26bda Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 16 Oct 2017 08:09:43 -0700 Subject: parallel tests (#7629) --- Makefile | 2 +- api/apitestlib.go | 12 ++++++-- api/command_test.go | 5 +-- api/file_test.go | 5 +-- api/post_test.go | 29 ++++++++++++------ api/websocket_test.go | 5 +-- api4/apitestlib.go | 11 +++++-- api4/command_test.go | 7 +++-- app/app.go | 8 +++-- app/app_test.go | 48 +++++++++++++++++++++++++++++ app/apptestlib.go | 45 +++++++++++++++++++++++++-- app/options.go | 38 ++++++++++++++++++++--- app/server.go | 82 ++++++++++++++++++++++++++++++++++++++------------ utils/logger/logger.go | 12 +++++--- web/web_test.go | 54 +++++++++++++++++++++++++++++++-- 15 files changed, 302 insertions(+), 61 deletions(-) create mode 100644 app/app_test.go diff --git a/Makefile b/Makefile index 2643b8aa2..ac00067ac 100644 --- a/Makefile +++ b/Makefile @@ -312,7 +312,7 @@ test-te: do-cover-file @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) + $(GO) test $(GOFLAGS) -run=$(TESTS) $(TESTFLAGS) -v -timeout=2000s -covermode=count -coverpkg=$(ALL_PACKAGES_COMMA) -exec $(ROOT)/scripts/test-xprog.sh $(TE_PACKAGES) find . -name 'cprofile.out' -exec sh -c 'tail -n +2 {} >> cover.out ; rm "{}"' \; test-ee: do-cover-file diff --git a/api/apitestlib.go b/api/apitestlib.go index 48637ee59..5b5bfff19 100644 --- a/api/apitestlib.go +++ b/api/apitestlib.go @@ -4,6 +4,7 @@ package api import ( + "fmt" "net" "time" @@ -69,6 +70,10 @@ func setupTestHelper(enterprise bool) *TestHelper { var options []app.Option if testStore != nil { options = append(options, app.StoreOverride(testStore)) + options = append(options, app.ConfigOverride(func(cfg *model.Config) { + cfg.ServiceSettings.ListenAddress = new(string) + *cfg.ServiceSettings.ListenAddress = ":0" + })) } th := &TestHelper{ @@ -153,8 +158,9 @@ func (me *TestHelper) InitSystemAdmin() *TestHelper { func (me *TestHelper) waitForConnectivity() { for i := 0; i < 1000; i++ { - _, err := net.Dial("tcp", "localhost"+*utils.Cfg.ServiceSettings.ListenAddress) + conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%v", me.App.Srv.ListenAddr.Port)) if err == nil { + conn.Close() return } time.Sleep(time.Millisecond * 20) @@ -163,11 +169,11 @@ func (me *TestHelper) waitForConnectivity() { } func (me *TestHelper) CreateClient() *model.Client { - return model.NewClient("http://localhost" + *utils.Cfg.ServiceSettings.ListenAddress) + return model.NewClient(fmt.Sprintf("http://localhost:%v", me.App.Srv.ListenAddr.Port)) } func (me *TestHelper) CreateWebSocketClient() (*model.WebSocketClient, *model.AppError) { - return model.NewWebSocketClient("ws://localhost"+*utils.Cfg.ServiceSettings.ListenAddress, me.BasicClient.AuthToken) + return model.NewWebSocketClient(fmt.Sprintf("ws://localhost:%v", me.App.Srv.ListenAddr.Port), me.BasicClient.AuthToken) } func (me *TestHelper) CreateTeam(client *model.Client) *model.Team { diff --git a/api/command_test.go b/api/command_test.go index 9d146b189..948193852 100644 --- a/api/command_test.go +++ b/api/command_test.go @@ -4,6 +4,7 @@ package api import ( + "fmt" "strings" "testing" "time" @@ -257,7 +258,7 @@ func TestTestCommand(t *testing.T) { *utils.Cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost" cmd1 := &model.Command{ - URL: "http://localhost" + *utils.Cfg.ServiceSettings.ListenAddress + model.API_URL_SUFFIX_V3 + "/teams/command_test", + URL: fmt.Sprintf("http://localhost:%v", th.App.Srv.ListenAddr.Port) + model.API_URL_SUFFIX_V3 + "/teams/command_test", Method: model.COMMAND_METHOD_POST, Trigger: "testcommand", } @@ -290,7 +291,7 @@ func TestTestCommand(t *testing.T) { } cmd2 := &model.Command{ - URL: "http://localhost" + *utils.Cfg.ServiceSettings.ListenAddress + model.API_URL_SUFFIX_V3 + "/teams/command_test", + URL: fmt.Sprintf("http://localhost:%v", th.App.Srv.ListenAddr.Port) + model.API_URL_SUFFIX_V3 + "/teams/command_test", Method: model.COMMAND_METHOD_GET, Trigger: "test2", } diff --git a/api/file_test.go b/api/file_test.go index 405e3e7d1..2935f0804 100644 --- a/api/file_test.go +++ b/api/file_test.go @@ -516,10 +516,7 @@ func TestGetPublicFileOld(t *testing.T) { store.Must(th.App.Srv.Store.FileInfo().AttachToPost(fileId, th.BasicPost.Id)) // reconstruct old style of link - siteURL := *utils.Cfg.ServiceSettings.SiteURL - if siteURL == "" { - siteURL = "http://localhost" + *utils.Cfg.ServiceSettings.ListenAddress - } + siteURL := fmt.Sprintf("http://localhost:%v", th.App.Srv.ListenAddr.Port) link := generatePublicLinkOld(siteURL, th.BasicTeam.Id, channel.Id, th.BasicUser.Id, fileId+"/test.png") // Wait a bit for files to ready diff --git a/api/post_test.go b/api/post_test.go index f57c2e05c..e7c230963 100644 --- a/api/post_test.go +++ b/api/post_test.go @@ -1153,20 +1153,31 @@ func TestEmailMention(t *testing.T) { if !strings.ContainsAny(resultsMailbox[len(resultsMailbox)-1].To[0], th.BasicUser2.Email) { t.Fatal("Wrong To recipient") } else { - for i := 0; i < 5; i++ { - if resultsEmail, err := utils.GetMessageFromMailbox(th.BasicUser2.Email, resultsMailbox[len(resultsMailbox)-1].ID); err == nil { - if strings.Contains(resultsEmail.Body.Text, post1.Message) { - break - } else if i == 4 { - t.Log(resultsEmail.Body.Text) - t.Fatal("Received wrong Message") + for i := 0; i < 30; i++ { + for j := len(resultsMailbox) - 1; j >= 0; j-- { + isUser := false + for _, to := range resultsMailbox[j].To { + if to == "<"+th.BasicUser2.Email+">" { + isUser = true + } + } + if !isUser { + continue + } + if resultsEmail, err := utils.GetMessageFromMailbox(th.BasicUser2.Email, resultsMailbox[j].ID); err == nil { + if strings.Contains(resultsEmail.Body.Text, post1.Message) { + return + } else if i == 4 { + t.Log(resultsEmail.Body.Text) + t.Fatal("Received wrong Message") + } } - time.Sleep(100 * time.Millisecond) } + time.Sleep(100 * time.Millisecond) } + t.Fatal("Didn't receive message") } } - } func TestFuzzyPosts(t *testing.T) { diff --git a/api/websocket_test.go b/api/websocket_test.go index 161ea4960..42604124b 100644 --- a/api/websocket_test.go +++ b/api/websocket_test.go @@ -4,6 +4,7 @@ package api import ( + "fmt" //"encoding/json" //"net/http" "net/http" @@ -323,7 +324,7 @@ func TestWebsocketOriginSecurity(t *testing.T) { th := Setup().InitBasic() defer th.TearDown() - url := "ws://localhost" + *utils.Cfg.ServiceSettings.ListenAddress + url := fmt.Sprintf("ws://localhost:%v", th.App.Srv.ListenAddr.Port) // Should fail because origin doesn't match _, _, err := websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{ @@ -335,7 +336,7 @@ func TestWebsocketOriginSecurity(t *testing.T) { // We are not a browser so we can spoof this just fine _, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{ - "Origin": []string{"http://localhost" + *utils.Cfg.ServiceSettings.ListenAddress}, + "Origin": []string{fmt.Sprintf("http://localhost:%v", th.App.Srv.ListenAddr.Port)}, }) if err != nil { t.Fatal(err) diff --git a/api4/apitestlib.go b/api4/apitestlib.go index 80a44c13f..640f38fe6 100644 --- a/api4/apitestlib.go +++ b/api4/apitestlib.go @@ -81,6 +81,10 @@ func setupTestHelper(enterprise bool) *TestHelper { var options []app.Option if testStore != nil { options = append(options, app.StoreOverride(testStore)) + options = append(options, app.ConfigOverride(func(cfg *model.Config) { + cfg.ServiceSettings.ListenAddress = new(string) + *cfg.ServiceSettings.ListenAddress = ":0" + })) } th := &TestHelper{ @@ -221,8 +225,9 @@ func (me *TestHelper) InitSystemAdmin() *TestHelper { func (me *TestHelper) waitForConnectivity() { for i := 0; i < 1000; i++ { - _, err := net.Dial("tcp", "localhost"+*utils.Cfg.ServiceSettings.ListenAddress) + conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%v", me.App.Srv.ListenAddr.Port)) if err == nil { + conn.Close() return } time.Sleep(time.Millisecond * 20) @@ -231,11 +236,11 @@ func (me *TestHelper) waitForConnectivity() { } func (me *TestHelper) CreateClient() *model.Client4 { - return model.NewAPIv4Client("http://localhost" + *utils.Cfg.ServiceSettings.ListenAddress) + return model.NewAPIv4Client(fmt.Sprintf("http://localhost:%v", me.App.Srv.ListenAddr.Port)) } func (me *TestHelper) CreateWebSocketClient() (*model.WebSocketClient, *model.AppError) { - return model.NewWebSocketClient4("ws://localhost"+*utils.Cfg.ServiceSettings.ListenAddress, me.Client.AuthToken) + return model.NewWebSocketClient4(fmt.Sprintf("ws://localhost:%v", me.App.Srv.ListenAddr.Port), me.Client.AuthToken) } func (me *TestHelper) CreateUser() *model.User { diff --git a/api4/command_test.go b/api4/command_test.go index 9a6c9dc78..52ef0f841 100644 --- a/api4/command_test.go +++ b/api4/command_test.go @@ -4,6 +4,7 @@ package api4 import ( + "fmt" "strings" "testing" @@ -399,7 +400,7 @@ func TestExecuteCommand(t *testing.T) { postCmd := &model.Command{ CreatorId: th.BasicUser.Id, TeamId: th.BasicTeam.Id, - URL: "http://localhost" + *utils.Cfg.ServiceSettings.ListenAddress + model.API_URL_SUFFIX_V4 + "/teams/command_test", + URL: fmt.Sprintf("http://localhost:%v", th.App.Srv.ListenAddr.Port) + model.API_URL_SUFFIX_V4 + "/teams/command_test", Method: model.COMMAND_METHOD_POST, Trigger: "postcommand", } @@ -443,7 +444,7 @@ func TestExecuteCommand(t *testing.T) { getCmd := &model.Command{ CreatorId: th.BasicUser.Id, TeamId: th.BasicTeam.Id, - URL: "http://localhost" + *utils.Cfg.ServiceSettings.ListenAddress + model.API_URL_SUFFIX_V4 + "/teams/command_test", + URL: fmt.Sprintf("http://localhost:%v", th.App.Srv.ListenAddr.Port) + model.API_URL_SUFFIX_V4 + "/teams/command_test", Method: model.COMMAND_METHOD_GET, Trigger: "getcommand", } @@ -511,7 +512,7 @@ func TestExecuteCommandAgainstChannelOnAnotherTeam(t *testing.T) { postCmd := &model.Command{ CreatorId: th.BasicUser.Id, TeamId: team2.Id, - URL: "http://localhost" + *utils.Cfg.ServiceSettings.ListenAddress + model.API_URL_SUFFIX_V4 + "/teams/command_test", + URL: fmt.Sprintf("http://localhost:%v", th.App.Srv.ListenAddr.Port) + model.API_URL_SUFFIX_V4 + "/teams/command_test", Method: model.COMMAND_METHOD_POST, Trigger: "postcommand", } diff --git a/app/app.go b/app/app.go index 7b6499b1f..34c0721a0 100644 --- a/app/app.go +++ b/app/app.go @@ -47,7 +47,8 @@ type App struct { Mfa einterfaces.MfaInterface Saml einterfaces.SamlInterface - newStore func() store.Store + newStore func() store.Store + configOverride func(*model.Config) *model.Config } var appCount = 0 @@ -77,7 +78,7 @@ func New(options ...Option) *App { if app.newStore == nil { app.newStore = func() store.Store { - return store.NewLayeredStore(sqlstore.NewSqlSupplier(utils.Cfg.SqlSettings, app.Metrics), app.Metrics, app.Cluster) + return store.NewLayeredStore(sqlstore.NewSqlSupplier(app.Config().SqlSettings, app.Metrics), app.Metrics, app.Cluster) } } @@ -233,6 +234,9 @@ func (a *App) initEnterprise() { } func (a *App) Config() *model.Config { + if a.configOverride != nil { + return a.configOverride(utils.Cfg) + } return utils.Cfg } diff --git a/app/app_test.go b/app/app_test.go new file mode 100644 index 000000000..00d08fb14 --- /dev/null +++ b/app/app_test.go @@ -0,0 +1,48 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package app + +import ( + "flag" + "os" + "testing" + + l4g "github.com/alecthomas/log4go" + + "github.com/mattermost/mattermost-server/store/storetest" + "github.com/mattermost/mattermost-server/utils" +) + +func TestMain(m *testing.M) { + flag.Parse() + + // 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 != "." { + utils.TranslationsPreInit() + utils.LoadConfig("config.json") + l4g.Info("-test.run used, not creating temporary containers") + os.Exit(m.Run()) + } + + utils.TranslationsPreInit() + utils.LoadConfig("config.json") + utils.InitTranslations(utils.Cfg.LocalizationSettings) + + 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/app/apptestlib.go b/app/apptestlib.go index 09bf02d39..9c26e0bbb 100644 --- a/app/apptestlib.go +++ b/app/apptestlib.go @@ -7,6 +7,9 @@ import ( "time" "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" l4g "github.com/alecthomas/log4go" @@ -21,13 +24,47 @@ type TestHelper struct { BasicPost *model.Post } +type persistentTestStore struct { + 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 setupTestHelper(enterprise bool) *TestHelper { - utils.TranslationsPreInit() + if utils.T == nil { + utils.TranslationsPreInit() + } utils.LoadConfig("config.json") utils.InitTranslations(utils.Cfg.LocalizationSettings) + var options []Option + if testStore != nil { + options = append(options, StoreOverride(testStore)) + options = append(options, ConfigOverride(func(cfg *model.Config) { + cfg.ServiceSettings.ListenAddress = new(string) + *cfg.ServiceSettings.ListenAddress = ":0" + })) + } + th := &TestHelper{ - App: New(), + App: New(options...), } *utils.Cfg.TeamSettings.MaxUsersPerTeam = 50 @@ -188,4 +225,8 @@ func (me *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) { func (me *TestHelper) TearDown() { me.App.Shutdown() + if err := recover(); err != nil { + StopTestStore() + panic(err) + } } diff --git a/app/options.go b/app/options.go index e5ac85706..121bbbf80 100644 --- a/app/options.go +++ b/app/options.go @@ -4,25 +4,53 @@ package app import ( + "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/store" ) type Option func(a *App) +// By default, the app will use a global configuration file. This allows you to override all or part +// of that configuration. +// +// The override parameter must be a *model.Config, func(*model.Config), or func(*model.Config) *model.Config. +// +// XXX: Most code will not respect this at the moment. (We need to eliminate utils.Cfg first.) +func ConfigOverride(override interface{}) Option { + return func(a *App) { + switch o := override.(type) { + case *model.Config: + a.configOverride = func(*model.Config) *model.Config { + return o + } + case func(*model.Config): + a.configOverride = func(cfg *model.Config) *model.Config { + ret := *cfg + o(&ret) + return &ret + } + case func(*model.Config) *model.Config: + a.configOverride = o + default: + panic("invalid ConfigOverride") + } + } +} + // By default, the app will use the store specified by the configuration. This allows you to // construct an app with a different store. // -// The storeOrFactory parameter must be either a store.Store or func(App) store.Store. -func StoreOverride(storeOrFactory interface{}) Option { +// The override parameter must be either a store.Store or func(App) store.Store. +func StoreOverride(override interface{}) Option { return func(a *App) { - switch s := storeOrFactory.(type) { + switch o := override.(type) { case store.Store: a.newStore = func() store.Store { - return s + return o } case func(*App) store.Store: a.newStore = func() store.Store { - return s(a) + return o(a) } default: panic("invalid StoreOverride") diff --git a/app/server.go b/app/server.go index c509d0440..08772dce4 100644 --- a/app/server.go +++ b/app/server.go @@ -4,6 +4,7 @@ package app import ( + "context" "crypto/tls" "io" "io/ioutil" @@ -16,7 +17,6 @@ import ( "github.com/gorilla/handlers" "github.com/gorilla/mux" "github.com/rsc/letsencrypt" - "github.com/tylerb/graceful" "gopkg.in/throttled/throttled.v2" "gopkg.in/throttled/throttled.v2/store/memstore" @@ -29,7 +29,8 @@ type Server struct { Store store.Store WebSocketRouter *WebSocketRouter Router *mux.Router - GracefulServer *graceful.Server + Server *http.Server + ListenAddr *net.TCPAddr } var allowedMethods []string = []string{ @@ -152,16 +153,29 @@ func (a *App) StartServer() { handler = httpRateLimiter.RateLimit(handler) } - a.Srv.GracefulServer = &graceful.Server{ - Timeout: TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN, - Server: &http.Server{ - Addr: *utils.Cfg.ServiceSettings.ListenAddress, - Handler: handlers.RecoveryHandler(handlers.RecoveryLogger(&RecoveryLogger{}), handlers.PrintRecoveryStack(true))(handler), - ReadTimeout: time.Duration(*utils.Cfg.ServiceSettings.ReadTimeout) * time.Second, - WriteTimeout: time.Duration(*utils.Cfg.ServiceSettings.WriteTimeout) * time.Second, - }, + a.Srv.Server = &http.Server{ + Handler: handlers.RecoveryHandler(handlers.RecoveryLogger(&RecoveryLogger{}), handlers.PrintRecoveryStack(true))(handler), + ReadTimeout: time.Duration(*utils.Cfg.ServiceSettings.ReadTimeout) * time.Second, + WriteTimeout: time.Duration(*utils.Cfg.ServiceSettings.WriteTimeout) * time.Second, } - l4g.Info(utils.T("api.server.start_server.listening.info"), *utils.Cfg.ServiceSettings.ListenAddress) + + addr := *a.Config().ServiceSettings.ListenAddress + if addr == "" { + if *utils.Cfg.ServiceSettings.ConnectionSecurity == model.CONN_SECURITY_TLS { + addr = ":https" + } else { + addr = ":http" + } + } + + listener, err := net.Listen("tcp", addr) + if err != nil { + l4g.Critical(utils.T("api.server.start_server.starting.critical"), err) + return + } + a.Srv.ListenAddr = listener.Addr().(*net.TCPAddr) + + l4g.Info(utils.T("api.server.start_server.listening.info"), listener.Addr().String()) if *utils.Cfg.ServiceSettings.Forward80To443 { go func() { @@ -189,25 +203,55 @@ func (a *App) StartServer() { tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2") - err = a.Srv.GracefulServer.ListenAndServeTLSConfig(tlsConfig) + a.Srv.Server.TLSConfig = tlsConfig + err = a.Srv.Server.ServeTLS(listener, "", "") } else { - err = a.Srv.GracefulServer.ListenAndServeTLS(*utils.Cfg.ServiceSettings.TLSCertFile, *utils.Cfg.ServiceSettings.TLSKeyFile) + err = a.Srv.Server.ServeTLS(listener, *utils.Cfg.ServiceSettings.TLSCertFile, *utils.Cfg.ServiceSettings.TLSKeyFile) } } else { - err = a.Srv.GracefulServer.ListenAndServe() + err = a.Srv.Server.Serve(listener) } - if err != nil { + if err != nil && err != http.ErrServerClosed { l4g.Critical(utils.T("api.server.start_server.starting.critical"), err) time.Sleep(time.Second) } }() } +type tcpKeepAliveListener struct { + *net.TCPListener +} + +func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { + tc, err := ln.AcceptTCP() + if err != nil { + return + } + tc.SetKeepAlive(true) + tc.SetKeepAlivePeriod(3 * time.Minute) + return tc, nil +} + +func (a *App) Listen(addr string) (net.Listener, error) { + if addr == "" { + addr = ":http" + } + ln, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + return tcpKeepAliveListener{ln.(*net.TCPListener)}, nil +} + func (a *App) StopServer() { - if a.Srv.GracefulServer != nil { - a.Srv.GracefulServer.Stop(TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN) - <-a.Srv.GracefulServer.StopChan() - a.Srv.GracefulServer = nil + if a.Srv.Server != nil { + ctx, cancel := context.WithTimeout(context.Background(), TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN) + defer cancel() + if err := a.Srv.Server.Shutdown(ctx); err != nil { + l4g.Warn(err.Error()) + } + a.Srv.Server.Close() + a.Srv.Server = nil } } diff --git a/utils/logger/logger.go b/utils/logger/logger.go index 227cf405e..e81b24e9c 100644 --- a/utils/logger/logger.go +++ b/utils/logger/logger.go @@ -19,6 +19,7 @@ import ( ) // this pattern allows us to "mock" the underlying l4g code when unit testing +var logger l4g.Logger var debugLog = l4g.Debug var infoLog = l4g.Info var errorLog = l4g.Error @@ -50,10 +51,13 @@ func initL4g(logSettings model.LogSettings) { } // create a logger that writes JSON objects to a file, and override our log methods to use it - flw := NewJSONFileLogger(level, utils.GetLogFileLocation(logSettings.FileLocation)+".jsonl") - debugLog = flw.Debug - infoLog = flw.Info - errorLog = flw.Error + if logger != nil { + logger.Close() + } + logger = NewJSONFileLogger(level, utils.GetLogFileLocation(logSettings.FileLocation)+".jsonl") + debugLog = logger.Debug + infoLog = logger.Info + errorLog = logger.Error } } diff --git a/web/web_test.go b/web/web_test.go index 6cc75fac1..23b43ba93 100644 --- a/web/web_test.go +++ b/web/web_test.go @@ -4,6 +4,8 @@ package web import ( + "fmt" + "os" "testing" "github.com/mattermost/mattermost-server/api" @@ -11,23 +13,44 @@ import ( "github.com/mattermost/mattermost-server/app" "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.Client 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 + } +} + func Setup() *app.App { utils.TranslationsPreInit() utils.LoadConfig("config.json") utils.InitTranslations(utils.Cfg.LocalizationSettings) - a := app.New() + a := app.New(app.StoreOverride(testStore), app.ConfigOverride(func(cfg *model.Config) { + cfg.ServiceSettings.ListenAddress = new(string) + *cfg.ServiceSettings.ListenAddress = ":0" + })) a.StartServer() api4.Init(a, a.Srv.Router, false) api3 := api.Init(a, a.Srv.Router) Init(api3) - URL = "http://localhost" + *utils.Cfg.ServiceSettings.ListenAddress + URL = fmt.Sprintf("http://localhost:%v", a.Srv.ListenAddr.Port) ApiClient = model.NewClient(URL) a.Srv.Store.MarkSystemRanUnitTests() @@ -39,6 +62,10 @@ func Setup() *app.App { func TearDown(a *app.App) { a.Shutdown() + if err := recover(); err != nil { + StopTestStore() + panic(err) + } } /* Test disabled for now so we don't requrie the client to build. Maybe re-enable after client gets moved out. @@ -108,3 +135,26 @@ func TestIncomingWebhook(t *testing.T) { } } } + +func TestMain(m *testing.M) { + utils.TranslationsPreInit() + utils.LoadConfig("config.json") + utils.InitTranslations(utils.Cfg.LocalizationSettings) + + 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() +} -- cgit v1.2.3-1-g7c22