Files
mattermost/app/server_test.go
Ibrahim Serdar Acikgoz 83d31ec907 [MM-31573] update log levels for the app package (#16629)
* update log levels for the app package

* reflect review comments

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
2021-01-15 09:58:34 +03:00

473 lines
14 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
"bufio"
"crypto/tls"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/http/httptest"
"os"
"path"
"strconv"
"strings"
"testing"
"time"
"github.com/getsentry/sentry-go"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/v5/config"
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/store/storetest"
"github.com/mattermost/mattermost-server/v5/utils/fileutils"
)
func TestStartServerSuccess(t *testing.T) {
s, err := NewServer()
require.NoError(t, err)
s.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
serverErr := s.Start()
client := &http.Client{}
checkEndpoint(t, client, "http://localhost:"+strconv.Itoa(s.ListenAddr.Port)+"/", http.StatusNotFound)
s.Shutdown()
require.NoError(t, serverErr)
}
func TestReadReplicaDisabledBasedOnLicense(t *testing.T) {
t.Skip("TODO: fix flaky test")
cfg := model.Config{}
cfg.SetDefaults()
driverName := os.Getenv("MM_SQLSETTINGS_DRIVERNAME")
if driverName == "" {
driverName = model.DATABASE_DRIVER_POSTGRES
}
dsn := ""
if driverName == model.DATABASE_DRIVER_POSTGRES {
dsn = os.Getenv("TEST_DATABASE_POSTGRESQL_DSN")
} else {
dsn = os.Getenv("TEST_DATABASE_MYSQL_DSN")
}
cfg.SqlSettings = *storetest.MakeSqlSettings(driverName)
if dsn != "" {
cfg.SqlSettings.DataSource = &dsn
}
cfg.SqlSettings.DataSourceReplicas = []string{*cfg.SqlSettings.DataSource}
cfg.SqlSettings.DataSourceSearchReplicas = []string{*cfg.SqlSettings.DataSource}
t.Run("Read Replicas with no License", func(t *testing.T) {
s, err := NewServer(func(server *Server) error {
configStore := config.NewTestMemoryStore()
configStore.Set(&cfg)
server.configStore = configStore
return nil
})
require.NoError(t, err)
defer s.Shutdown()
require.Same(t, s.sqlStore.GetMaster(), s.sqlStore.GetReplica())
require.Len(t, s.Config().SqlSettings.DataSourceReplicas, 1)
})
t.Run("Read Replicas With License", func(t *testing.T) {
s, err := NewServer(func(server *Server) error {
configStore := config.NewTestMemoryStore()
configStore.Set(&cfg)
server.licenseValue.Store(model.NewTestLicense())
return nil
})
require.NoError(t, err)
defer s.Shutdown()
require.NotSame(t, s.sqlStore.GetMaster(), s.sqlStore.GetReplica())
require.Len(t, s.Config().SqlSettings.DataSourceReplicas, 1)
})
t.Run("Search Replicas with no License", func(t *testing.T) {
s, err := NewServer(func(server *Server) error {
configStore := config.NewTestMemoryStore()
configStore.Set(&cfg)
server.configStore = configStore
return nil
})
require.NoError(t, err)
defer s.Shutdown()
require.Same(t, s.sqlStore.GetMaster(), s.sqlStore.GetSearchReplica())
require.Len(t, s.Config().SqlSettings.DataSourceSearchReplicas, 1)
})
t.Run("Search Replicas With License", func(t *testing.T) {
s, err := NewServer(func(server *Server) error {
configStore := config.NewTestMemoryStore()
configStore.Set(&cfg)
server.configStore = configStore
server.licenseValue.Store(model.NewTestLicense())
return nil
})
require.NoError(t, err)
defer s.Shutdown()
require.NotSame(t, s.sqlStore.GetMaster(), s.sqlStore.GetSearchReplica())
require.Len(t, s.Config().SqlSettings.DataSourceSearchReplicas, 1)
})
}
func TestStartServerPortUnavailable(t *testing.T) {
s, err := NewServer()
require.NoError(t, err)
// Listen on the next available port
listener, err := net.Listen("tcp", ":0")
require.NoError(t, err)
// Attempt to listen on the port used above.
s.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ListenAddress = listener.Addr().String()
})
serverErr := s.Start()
s.Shutdown()
require.Error(t, serverErr)
}
func TestStartServerTLSSuccess(t *testing.T) {
s, err := NewServer()
require.NoError(t, err)
testDir, _ := fileutils.FindDir("tests")
s.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
*cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem")
*cfg.ServiceSettings.TLSCertFile = path.Join(testDir, "tls_test_cert.pem")
})
serverErr := s.Start()
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
checkEndpoint(t, client, "https://localhost:"+strconv.Itoa(s.ListenAddr.Port)+"/", http.StatusNotFound)
s.Shutdown()
require.NoError(t, serverErr)
}
func TestStartServerTLSVersion(t *testing.T) {
s, err := NewServer()
require.NoError(t, err)
testDir, _ := fileutils.FindDir("tests")
s.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
*cfg.ServiceSettings.TLSMinVer = "1.2"
*cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem")
*cfg.ServiceSettings.TLSCertFile = path.Join(testDir, "tls_test_cert.pem")
})
serverErr := s.Start()
tr := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
MaxVersion: tls.VersionTLS11,
},
}
client := &http.Client{Transport: tr}
err = checkEndpoint(t, client, "https://localhost:"+strconv.Itoa(s.ListenAddr.Port)+"/", http.StatusNotFound)
if !strings.Contains(err.Error(), "remote error: tls: protocol version not supported") {
t.Errorf("Expected protocol version error, got %s", err)
}
client.Transport = &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
err = checkEndpoint(t, client, "https://localhost:"+strconv.Itoa(s.ListenAddr.Port)+"/", http.StatusNotFound)
if err != nil {
t.Errorf("Expected nil, got %s", err)
}
s.Shutdown()
require.NoError(t, serverErr)
}
func TestStartServerTLSOverwriteCipher(t *testing.T) {
s, err := NewServer()
require.NoError(t, err)
testDir, _ := fileutils.FindDir("tests")
s.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
cfg.ServiceSettings.TLSOverwriteCiphers = []string{
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
}
*cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem")
*cfg.ServiceSettings.TLSCertFile = path.Join(testDir, "tls_test_cert.pem")
})
serverErr := s.Start()
tr := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
CipherSuites: []uint16{
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
},
MaxVersion: tls.VersionTLS12,
},
}
client := &http.Client{Transport: tr}
err = checkEndpoint(t, client, "https://localhost:"+strconv.Itoa(s.ListenAddr.Port)+"/", http.StatusNotFound)
require.Error(t, err, "Expected error due to Cipher mismatch")
if !strings.Contains(err.Error(), "remote error: tls: handshake failure") {
t.Errorf("Expected protocol version error, got %s", err)
}
client.Transport = &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
},
MaxVersion: tls.VersionTLS12,
},
}
err = checkEndpoint(t, client, "https://localhost:"+strconv.Itoa(s.ListenAddr.Port)+"/", http.StatusNotFound)
if err != nil {
t.Errorf("Expected nil, got %s", err)
}
s.Shutdown()
require.NoError(t, serverErr)
}
func checkEndpoint(t *testing.T, client *http.Client, url string, expectedStatus int) error {
res, err := client.Get(url)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != expectedStatus {
t.Errorf("Response code was %d; want %d", res.StatusCode, expectedStatus)
}
return nil
}
func TestPanicLog(t *testing.T) {
// Creating a temp file to collect logs
tmpfile, err := ioutil.TempFile("", "mlog")
if err != nil {
require.NoError(t, err)
}
defer func() {
require.NoError(t, tmpfile.Close())
require.NoError(t, os.Remove(tmpfile.Name()))
}()
// This test requires Zap file target for now.
mlog.EnableZap()
defer mlog.DisableZap()
// Creating logger to log to console and temp file
logger := mlog.NewLogger(&mlog.LoggerConfiguration{
EnableConsole: true,
ConsoleJson: true,
EnableFile: true,
FileLocation: tmpfile.Name(),
FileLevel: mlog.LevelInfo,
})
// Creating a server with logger
s, err := NewServer(SetLogger(logger))
require.NoError(t, err)
// Route for just panicing
s.Router.HandleFunc("/panic", func(writer http.ResponseWriter, request *http.Request) {
s.Log.Info("inside panic handler")
panic("log this panic")
})
testDir, _ := fileutils.FindDir("tests")
s.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
*cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem")
*cfg.ServiceSettings.TLSCertFile = path.Join(testDir, "tls_test_cert.pem")
})
serverErr := s.Start()
require.NoError(t, serverErr)
// Calling panic route
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
client.Get("https://localhost:" + strconv.Itoa(s.ListenAddr.Port) + "/panic")
s.Shutdown()
// Checking whether panic was logged
var panicLogged = false
var infoLogged = false
_, err = tmpfile.Seek(0, 0)
require.NoError(t, err)
scanner := bufio.NewScanner(tmpfile)
for scanner.Scan() {
if !infoLogged && strings.Contains(scanner.Text(), "inside panic handler") {
infoLogged = true
}
if strings.Contains(scanner.Text(), "log this panic") {
panicLogged = true
break
}
}
if !infoLogged {
t.Error("Info log line was supposed to be logged")
}
if !panicLogged {
t.Error("Panic was supposed to be logged")
}
}
func TestSentry(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
client := &http.Client{Timeout: 5 * time.Second, Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}}
testDir, _ := fileutils.FindDir("tests")
t.Run("sentry is disabled, should not receive a report", func(t *testing.T) {
data := make(chan bool, 1)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Log("Received sentry request for some reason")
data <- true
}))
defer server.Close()
// make sure we don't report anything when sentry is disabled
_, port, _ := net.SplitHostPort(server.Listener.Addr().String())
dsn, err := sentry.NewDsn(fmt.Sprintf("http://test:test@localhost:%s/123", port))
require.NoError(t, err)
SentryDSN = dsn.String()
s, err := NewServer(func(server *Server) error {
configStore, _ := config.NewFileStore("config.json", true)
store, _ := config.NewStoreFromBacking(configStore, nil)
server.configStore = store
server.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.LogSettings.EnableSentry = false
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
*cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem")
*cfg.ServiceSettings.TLSCertFile = path.Join(testDir, "tls_test_cert.pem")
*cfg.LogSettings.EnableDiagnostics = true
})
return nil
})
require.NoError(t, err)
// Route for just panicing
s.Router.HandleFunc("/panic", func(writer http.ResponseWriter, request *http.Request) {
panic("log this panic")
})
require.NoError(t, s.Start())
defer s.Shutdown()
resp, err := client.Get("https://localhost:" + strconv.Itoa(s.ListenAddr.Port) + "/panic")
require.Nil(t, resp)
require.True(t, errors.Is(err, io.EOF), fmt.Sprintf("unexpected error: %s", err))
sentry.Flush(time.Second)
select {
case <-data:
require.Fail(t, "Sentry received a message, even though it's disabled!")
case <-time.After(time.Second):
t.Log("Sentry request didn't arrive. Good!")
}
})
t.Run("sentry is enabled, report should be received", func(t *testing.T) {
data := make(chan bool, 1)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Log("Received sentry request!")
data <- true
}))
defer server.Close()
_, port, _ := net.SplitHostPort(server.Listener.Addr().String())
dsn, err := sentry.NewDsn(fmt.Sprintf("http://test:test@localhost:%s/123", port))
require.NoError(t, err)
SentryDSN = dsn.String()
s, err := NewServer(func(server *Server) error {
configStore, _ := config.NewFileStore("config.json", true)
store, _ := config.NewStoreFromBacking(configStore, nil)
server.configStore = store
server.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
*cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem")
*cfg.ServiceSettings.TLSCertFile = path.Join(testDir, "tls_test_cert.pem")
*cfg.LogSettings.EnableSentry = true
*cfg.LogSettings.EnableDiagnostics = true
})
return nil
})
require.NoError(t, err)
// Route for just panicing
s.Router.HandleFunc("/panic", func(writer http.ResponseWriter, request *http.Request) {
panic("log this panic")
})
require.NoError(t, s.Start())
defer s.Shutdown()
resp, err := client.Get("https://localhost:" + strconv.Itoa(s.ListenAddr.Port) + "/panic")
require.Nil(t, resp)
require.True(t, errors.Is(err, io.EOF), fmt.Sprintf("unexpected error: %s", err))
sentry.Flush(time.Second)
select {
case <-data:
t.Log("Sentry request arrived. Good!")
case <-time.After(time.Second * 10):
require.Fail(t, "Sentry report didn't arrive")
}
})
}