* Move Channels into App In this PR, we make Channels as part of App instead of Server. This is part of the transition period of moving fields from Server to Channels. For now, Channels contains Server. So the hierarchy is App -> Channels -> Server. And as a first step, we also move httpService to Channels. ```release-note NONE ``` * Fixing another test ```release-note NONE ``` * new method ```release-note NONE ```
793 строки
24 KiB
Go
793 строки
24 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/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/mattermost/mattermost-server/v6/app/users"
|
|
"github.com/mattermost/mattermost-server/v6/config"
|
|
"github.com/mattermost/mattermost-server/v6/model"
|
|
"github.com/mattermost/mattermost-server/v6/shared/filestore"
|
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
|
"github.com/mattermost/mattermost-server/v6/store/storetest"
|
|
"github.com/mattermost/mattermost-server/v6/utils/fileutils"
|
|
)
|
|
|
|
func newServerWithConfig(t *testing.T, f func(cfg *model.Config)) (*Server, error) {
|
|
configStore, err := config.NewMemoryStore()
|
|
require.NoError(t, err)
|
|
store, err := config.NewStoreFromBacking(configStore, nil, false)
|
|
require.NoError(t, err)
|
|
cfg := store.Get()
|
|
f(cfg)
|
|
|
|
store.Set(cfg)
|
|
|
|
return NewServer(ConfigStore(store))
|
|
}
|
|
|
|
func TestStartServerSuccess(t *testing.T) {
|
|
s, err := newServerWithConfig(t, func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.ListenAddress = ":0"
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
serverErr := s.Start()
|
|
|
|
client := &http.Client{}
|
|
checkEndpoint(t, client, "http://localhost:"+strconv.Itoa(s.ListenAddr.Port)+"/")
|
|
|
|
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.DatabaseDriverPostgres
|
|
}
|
|
dsn := ""
|
|
if driverName == model.DatabaseDriverPostgres {
|
|
dsn = os.Getenv("TEST_DATABASE_POSTGRESQL_DSN")
|
|
} else {
|
|
dsn = os.Getenv("TEST_DATABASE_MYSQL_DSN")
|
|
}
|
|
cfg.SqlSettings = *storetest.MakeSqlSettings(driverName, false)
|
|
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 TestStartServerNoS3Bucket(t *testing.T) {
|
|
s3Host := os.Getenv("CI_MINIO_HOST")
|
|
if s3Host == "" {
|
|
s3Host = "localhost"
|
|
}
|
|
|
|
s3Port := os.Getenv("CI_MINIO_PORT")
|
|
if s3Port == "" {
|
|
s3Port = "9000"
|
|
}
|
|
|
|
s3Endpoint := fmt.Sprintf("%s:%s", s3Host, s3Port)
|
|
|
|
s, err := NewServer(func(server *Server) error {
|
|
configStore, _ := config.NewFileStore("config.json")
|
|
store, _ := config.NewStoreFromBacking(configStore, nil, false)
|
|
server.configStore = store
|
|
server.UpdateConfig(func(cfg *model.Config) {
|
|
cfg.FileSettings = model.FileSettings{
|
|
DriverName: model.NewString(model.ImageDriverS3),
|
|
AmazonS3AccessKeyId: model.NewString(model.MinioAccessKey),
|
|
AmazonS3SecretAccessKey: model.NewString(model.MinioSecretKey),
|
|
AmazonS3Bucket: model.NewString("nosuchbucket"),
|
|
AmazonS3Endpoint: model.NewString(s3Endpoint),
|
|
AmazonS3Region: model.NewString(""),
|
|
AmazonS3PathPrefix: model.NewString(""),
|
|
AmazonS3SSL: model.NewBool(false),
|
|
}
|
|
})
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// ensure that a new bucket was created
|
|
backend, appErr := s.FileBackend()
|
|
require.Nil(t, appErr)
|
|
err = backend.(*filestore.S3FileBackend).TestConnection()
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestStartServerTLSSuccess(t *testing.T) {
|
|
s, err := newServerWithConfig(t, func(cfg *model.Config) {
|
|
testDir, _ := fileutils.FindDir("tests")
|
|
|
|
*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")
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
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)+"/")
|
|
|
|
s.Shutdown()
|
|
require.NoError(t, serverErr)
|
|
}
|
|
|
|
func TestDatabaseTypeAndMattermostVersion(t *testing.T) {
|
|
sqlDrivernameEnvironment := os.Getenv("MM_SQLSETTINGS_DRIVERNAME")
|
|
|
|
if sqlDrivernameEnvironment != "" {
|
|
defer os.Setenv("MM_SQLSETTINGS_DRIVERNAME", sqlDrivernameEnvironment)
|
|
} else {
|
|
defer os.Unsetenv("MM_SQLSETTINGS_DRIVERNAME")
|
|
}
|
|
|
|
os.Setenv("MM_SQLSETTINGS_DRIVERNAME", "postgres")
|
|
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
databaseType, mattermostVersion := th.Server.DatabaseTypeAndMattermostVersion()
|
|
assert.Equal(t, "postgres", databaseType)
|
|
assert.Equal(t, "5.31.0", mattermostVersion)
|
|
|
|
os.Setenv("MM_SQLSETTINGS_DRIVERNAME", "mysql")
|
|
|
|
th2 := Setup(t)
|
|
defer th2.TearDown()
|
|
|
|
databaseType, mattermostVersion = th2.Server.DatabaseTypeAndMattermostVersion()
|
|
assert.Equal(t, "mysql", databaseType)
|
|
assert.Equal(t, "5.31.0", mattermostVersion)
|
|
}
|
|
|
|
func TestGenerateSupportPacket(t *testing.T) {
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
d1 := []byte("hello\ngo\n")
|
|
err := ioutil.WriteFile("mattermost.log", d1, 0777)
|
|
require.NoError(t, err)
|
|
err = ioutil.WriteFile("notifications.log", d1, 0777)
|
|
require.NoError(t, err)
|
|
|
|
fileDatas := th.App.GenerateSupportPacket()
|
|
testFiles := []string{"support_packet.yaml", "plugins.json", "sanitized_config.json", "mattermost.log", "notifications.log"}
|
|
for i, fileData := range fileDatas {
|
|
require.NotNil(t, fileData)
|
|
assert.Equal(t, testFiles[i], fileData.Filename)
|
|
assert.Positive(t, len(fileData.Body))
|
|
}
|
|
|
|
// Remove these two files and ensure that warning.txt file is generated
|
|
err = os.Remove("notifications.log")
|
|
require.NoError(t, err)
|
|
err = os.Remove("mattermost.log")
|
|
require.NoError(t, err)
|
|
fileDatas = th.App.GenerateSupportPacket()
|
|
testFiles = []string{"support_packet.yaml", "plugins.json", "sanitized_config.json", "warning.txt"}
|
|
for i, fileData := range fileDatas {
|
|
require.NotNil(t, fileData)
|
|
assert.Equal(t, testFiles[i], fileData.Filename)
|
|
assert.Positive(t, len(fileData.Body))
|
|
}
|
|
}
|
|
|
|
func TestGetNotificationsLog(t *testing.T) {
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
// Disable notifications file to get an error
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.NotificationLogSettings.EnableFile = false
|
|
})
|
|
|
|
fileData, warning := th.App.getNotificationsLog()
|
|
assert.Nil(t, fileData)
|
|
assert.Equal(t, warning, "Unable to retrieve notifications.log because LogSettings: EnableFile is false in config.json")
|
|
|
|
// Enable notifications file but delete any notifications file to get an error trying to read the file
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.NotificationLogSettings.EnableFile = true
|
|
})
|
|
|
|
// If any previous notifications.log file, lets delete it
|
|
os.Remove("notifications.log")
|
|
|
|
fileData, warning = th.App.getNotificationsLog()
|
|
assert.Nil(t, fileData)
|
|
assert.Contains(t, warning, "ioutil.ReadFile(notificationsLog) Error:")
|
|
|
|
// Happy path where we have file and no warning
|
|
d1 := []byte("hello\ngo\n")
|
|
err := ioutil.WriteFile("notifications.log", d1, 0777)
|
|
defer os.Remove("notifications.log")
|
|
require.NoError(t, err)
|
|
|
|
fileData, warning = th.App.getNotificationsLog()
|
|
require.NotNil(t, fileData)
|
|
assert.Equal(t, "notifications.log", fileData.Filename)
|
|
assert.Positive(t, len(fileData.Body))
|
|
assert.Empty(t, warning)
|
|
}
|
|
|
|
func TestGetMattermostLog(t *testing.T) {
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
// disable mattermost log file setting in config so we should get an warning
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.LogSettings.EnableFile = false
|
|
})
|
|
|
|
fileData, warning := th.App.getMattermostLog()
|
|
assert.Nil(t, fileData)
|
|
assert.Equal(t, "Unable to retrieve mattermost.log because LogSettings: EnableFile is false in config.json", warning)
|
|
|
|
// We enable the setting but delete any mattermost log file
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.LogSettings.EnableFile = true
|
|
})
|
|
|
|
// If any previous mattermost.log file, lets delete it
|
|
os.Remove("mattermost.log")
|
|
|
|
fileData, warning = th.App.getMattermostLog()
|
|
assert.Nil(t, fileData)
|
|
assert.Contains(t, warning, "ioutil.ReadFile(mattermostLog) Error:")
|
|
|
|
// Happy path where we get a log file and no warning
|
|
d1 := []byte("hello\ngo\n")
|
|
err := ioutil.WriteFile("mattermost.log", d1, 0777)
|
|
defer os.Remove("mattermost.log")
|
|
require.NoError(t, err)
|
|
|
|
fileData, warning = th.App.getMattermostLog()
|
|
require.NotNil(t, fileData)
|
|
assert.Equal(t, "mattermost.log", fileData.Filename)
|
|
assert.Positive(t, len(fileData.Body))
|
|
assert.Empty(t, warning)
|
|
}
|
|
|
|
func TestCreateSanitizedConfigFile(t *testing.T) {
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
// Happy path where we have a sanitized config file with no warning
|
|
fileData, warning := th.App.createSanitizedConfigFile()
|
|
require.NotNil(t, fileData)
|
|
assert.Equal(t, "sanitized_config.json", fileData.Filename)
|
|
assert.Positive(t, len(fileData.Body))
|
|
assert.Empty(t, warning)
|
|
}
|
|
|
|
func TestCreatePluginsFile(t *testing.T) {
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
// Happy path where we have a plugins file with no warning
|
|
fileData, warning := th.App.createPluginsFile()
|
|
require.NotNil(t, fileData)
|
|
assert.Equal(t, "plugins.json", fileData.Filename)
|
|
assert.Positive(t, len(fileData.Body))
|
|
assert.Empty(t, warning)
|
|
|
|
// Turn off plugins so we can get an error
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.PluginSettings.Enable = false
|
|
})
|
|
|
|
// Plugins off in settings so no fileData and we get a warning instead
|
|
fileData, warning = th.App.createPluginsFile()
|
|
assert.Nil(t, fileData)
|
|
assert.Contains(t, warning, "c.App.GetPlugins() Error:")
|
|
}
|
|
|
|
func TestGenerateSupportPacketYaml(t *testing.T) {
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
// Happy path where we have a support packet yaml file without any warnings
|
|
fileData, warning := th.App.generateSupportPacketYaml()
|
|
require.NotNil(t, fileData)
|
|
assert.Equal(t, "support_packet.yaml", fileData.Filename)
|
|
assert.Positive(t, len(fileData.Body))
|
|
assert.Empty(t, warning)
|
|
|
|
}
|
|
|
|
func TestStartServerTLSVersion(t *testing.T) {
|
|
configStore, _ := config.NewMemoryStore()
|
|
store, _ := config.NewStoreFromBacking(configStore, nil, false)
|
|
cfg := store.Get()
|
|
testDir, _ := fileutils.FindDir("tests")
|
|
|
|
*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")
|
|
|
|
store.Set(cfg)
|
|
|
|
s, err := NewServer(ConfigStore(store))
|
|
require.NoError(t, err)
|
|
|
|
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)+"/")
|
|
|
|
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)+"/")
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected nil, got %s", err)
|
|
}
|
|
|
|
s.Shutdown()
|
|
require.NoError(t, serverErr)
|
|
}
|
|
|
|
func TestStartServerTLSOverwriteCipher(t *testing.T) {
|
|
s, err := newServerWithConfig(t, func(cfg *model.Config) {
|
|
testDir, _ := fileutils.FindDir("tests")
|
|
|
|
*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")
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = s.Start()
|
|
require.NoError(t, err)
|
|
|
|
defer s.Shutdown()
|
|
|
|
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)+"/")
|
|
require.Error(t, err, "Expected error due to Cipher mismatch")
|
|
require.Contains(t, err.Error(), "remote error: tls: handshake failure", "Expected protocol version error")
|
|
|
|
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)+"/")
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func checkEndpoint(t *testing.T, client *http.Client, url string) error {
|
|
res, err := client.Get(url)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode != http.StatusNotFound {
|
|
t.Errorf("Response code was %d; want %d", res.StatusCode, http.StatusNotFound)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func TestPanicLog(t *testing.T) {
|
|
// Creating a temp dir for log
|
|
tmpDir, err := os.MkdirTemp("", "mlog-test")
|
|
require.NoError(t, err, "cannot create tmp dir for log file")
|
|
defer func() {
|
|
err2 := os.RemoveAll(tmpDir)
|
|
assert.NoError(t, err2)
|
|
}()
|
|
|
|
// Creating logger to log to console and temp file
|
|
logger, _ := mlog.NewLogger()
|
|
|
|
logSettings := model.NewLogSettings()
|
|
logSettings.EnableConsole = model.NewBool(true)
|
|
logSettings.ConsoleJson = model.NewBool(true)
|
|
logSettings.EnableFile = model.NewBool(true)
|
|
logSettings.FileLocation = &tmpDir
|
|
logSettings.FileLevel = &mlog.LvlInfo.Name
|
|
|
|
cfg, err := config.MloggerConfigFromLoggerConfig(logSettings, nil, config.GetLogFileLocation)
|
|
require.NoError(t, err)
|
|
err = logger.ConfigureTargets(cfg, nil)
|
|
require.NoError(t, err)
|
|
logger.LockConfiguration()
|
|
|
|
// 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")
|
|
|
|
err = logger.Flush()
|
|
assert.NoError(t, err, "flush should succeed")
|
|
s.Shutdown()
|
|
|
|
// Checking whether panic was logged
|
|
var panicLogged = false
|
|
var infoLogged = false
|
|
|
|
logFile, err := os.Open(config.GetLogFileLocation(tmpDir))
|
|
require.NoError(t, err, "cannot open log file")
|
|
|
|
_, err = logFile.Seek(0, 0)
|
|
require.NoError(t, err)
|
|
|
|
scanner := bufio.NewScanner(logFile)
|
|
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 := newServerWithConfig(t, 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
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
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 := newServerWithConfig(t, 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
|
|
})
|
|
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")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestAdminAdvisor(t *testing.T) {
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
// creating a system user to whole admin advisor will send post
|
|
user := model.User{
|
|
Email: strings.ToLower(model.NewId()) + "success+test@example.com",
|
|
Nickname: "Darth Vader",
|
|
Username: "vader" + model.NewId(),
|
|
Password: "passwd1",
|
|
AuthService: "",
|
|
Roles: model.SystemAdminRoleId,
|
|
}
|
|
ruser, err := th.App.CreateUser(th.Context, &user)
|
|
assert.Nil(t, err, "User should be created")
|
|
defer th.App.PermanentDeleteUser(th.Context, &user)
|
|
|
|
t.Run("Should notify admin of un-configured support email", func(t *testing.T) {
|
|
doCheckAdminSupportStatus(th.App, th.Context)
|
|
|
|
bot, err := th.App.GetUserByUsername(model.BotWarnMetricBotUsername)
|
|
assert.NotNil(t, bot, "Bot should have been created now")
|
|
assert.Nil(t, err, "No error should be generated")
|
|
|
|
channel, err := th.App.getDirectChannel(bot.Id, ruser.Id)
|
|
assert.NotNil(t, channel, "DM channel should exist between Admin Advisor and system admin")
|
|
assert.Nil(t, err, "No error should be generated")
|
|
})
|
|
|
|
t.Run("Should NOT notify admin when support email is configured", func(t *testing.T) {
|
|
th.App.UpdateConfig(func(m *model.Config) {
|
|
email := "success+test@example.com"
|
|
m.SupportSettings.SupportEmail = &email
|
|
})
|
|
|
|
bot, err := th.App.GetUserByUsername(model.BotWarnMetricBotUsername)
|
|
assert.NotNil(t, bot, "Bot should be already created")
|
|
assert.Nil(t, err, "No error should be generated")
|
|
|
|
channel, err := th.App.getDirectChannel(bot.Id, ruser.Id)
|
|
assert.NotNil(t, channel, "DM channel should already exist")
|
|
assert.Nil(t, err, "No error should be generated")
|
|
|
|
err = th.App.PermanentDeleteChannel(channel)
|
|
assert.Nil(t, err, "No error should be generated")
|
|
|
|
doCheckAdminSupportStatus(th.App, th.Context)
|
|
|
|
channel, err = th.App.getDirectChannel(bot.Id, ruser.Id)
|
|
assert.NotNil(t, channel, "DM channel should exist between Admin Advisor and system admin")
|
|
assert.Nil(t, err, "No error should be generated")
|
|
|
|
posts, err := th.App.GetPosts(channel.Id, 0, 100)
|
|
assert.Nil(t, err, "No error should be generated")
|
|
assert.Equal(t, 0, len(posts.Posts))
|
|
})
|
|
|
|
t.Run("Should not break in case of many sysadmins", func(t *testing.T) {
|
|
var userList []*model.User
|
|
for i := 0; i < 50; i++ {
|
|
user := model.User{
|
|
Email: strings.ToLower(NewTestId()) + "success+test@example.com",
|
|
Nickname: "Admin",
|
|
Username: "admin" + NewTestId(),
|
|
Password: "password",
|
|
AuthService: "",
|
|
Roles: model.SystemAdminRoleId + " " + model.SystemUserRoleId,
|
|
}
|
|
ruser, err := th.App.ch.srv.userService.CreateUser(&user, users.UserCreateOptions{FromImport: true})
|
|
assert.NoError(t, err, "User should be created")
|
|
userList = append(userList, ruser)
|
|
defer th.App.PermanentDeleteUser(th.Context, ruser)
|
|
}
|
|
|
|
th.App.notifyAdminsOfWarnMetricStatus(th.Context, model.SystemMetricSupportEmailNotConfigured, true)
|
|
|
|
bot, err := th.App.GetUserByUsername(model.BotWarnMetricBotUsername)
|
|
assert.NotNil(t, bot, "Bot should have been created now")
|
|
assert.Nil(t, err, "No error should be generated")
|
|
|
|
for _, user := range userList {
|
|
channel, err := th.App.getDirectChannel(bot.Id, user.Id)
|
|
assert.NotNil(t, channel, "DM channel should exist between Admin Advisor and system admin")
|
|
assert.Nil(t, err, "No error should be generated")
|
|
}
|
|
})
|
|
}
|