Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2591 lines
73 KiB
Go
2591 lines
73 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package model
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"maps"
|
|
"os"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestConfigDefaults(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("somewhere nil when uninitialized", func(t *testing.T) {
|
|
c := Config{}
|
|
require.False(t, checkNowhereNil(t, "config", c))
|
|
})
|
|
|
|
t.Run("nowhere nil when initialized", func(t *testing.T) {
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
require.True(t, checkNowhereNil(t, "config", c))
|
|
})
|
|
|
|
t.Run("nowhere nil when partially initialized", func(t *testing.T) {
|
|
var recursivelyUninitialize func(*Config, string, reflect.Value)
|
|
recursivelyUninitialize = func(config *Config, name string, v reflect.Value) {
|
|
if v.Type().Kind() == reflect.Ptr {
|
|
// Ignoring these 2 settings.
|
|
// TODO: remove them completely in v8.0.
|
|
if name == "config.ElasticsearchSettings.BulkIndexingTimeWindowSeconds" ||
|
|
name == "config.ClusterSettings.EnableExperimentalGossipEncryption" {
|
|
return
|
|
}
|
|
|
|
// Set every pointer we find in the tree to nil
|
|
v.Set(reflect.Zero(v.Type()))
|
|
require.True(t, v.IsNil())
|
|
|
|
// SetDefaults on the root config should make it non-nil, otherwise
|
|
// it means that SetDefaults isn't being called recursively in
|
|
// all cases.
|
|
config.SetDefaults()
|
|
if assert.False(t, v.IsNil(), "%s should be non-nil after SetDefaults()", name) {
|
|
recursivelyUninitialize(config, fmt.Sprintf("(*%s)", name), v.Elem())
|
|
}
|
|
} else if v.Type().Kind() == reflect.Struct {
|
|
for i := 0; i < v.NumField(); i++ {
|
|
recursivelyUninitialize(config, fmt.Sprintf("%s.%s", name, v.Type().Field(i).Name), v.Field(i))
|
|
}
|
|
}
|
|
}
|
|
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
recursivelyUninitialize(&c, "config", reflect.ValueOf(&c).Elem())
|
|
})
|
|
t.Run("report a problem defaults", func(t *testing.T) {
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
require.Equal(t, SupportSettingsDefaultReportAProblemType, *c.SupportSettings.ReportAProblemType)
|
|
require.Equal(t, SupportSettingsDefaultReportAProblemLink, *c.SupportSettings.ReportAProblemLink)
|
|
require.Equal(t, "", *c.SupportSettings.ReportAProblemMail)
|
|
require.Equal(t, true, *c.SupportSettings.AllowDownloadLogs)
|
|
})
|
|
}
|
|
|
|
func TestConfigIsValid(t *testing.T) {
|
|
t.Run("report a problem values", func(t *testing.T) {
|
|
t.Run("email", func(t *testing.T) {
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
c.SupportSettings.ReportAProblemType = NewPointer(string(SupportSettingsReportAProblemTypeMail))
|
|
c.SupportSettings.ReportAProblemMail = nil
|
|
require.NotNil(t, c.IsValid())
|
|
|
|
c.SupportSettings.ReportAProblemMail = NewPointer("")
|
|
require.NotNil(t, c.IsValid())
|
|
|
|
c.SupportSettings.ReportAProblemMail = NewPointer("invalid")
|
|
require.NotNil(t, c.IsValid())
|
|
|
|
c.SupportSettings.ReportAProblemMail = NewPointer("valid@email.com")
|
|
require.Nil(t, c.IsValid())
|
|
})
|
|
|
|
t.Run("link", func(t *testing.T) {
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
c.SupportSettings.ReportAProblemType = NewPointer(string(SupportSettingsReportAProblemTypeLink))
|
|
c.SupportSettings.ReportAProblemLink = nil
|
|
require.NotNil(t, c.IsValid())
|
|
|
|
c.SupportSettings.ReportAProblemLink = NewPointer("")
|
|
require.NotNil(t, c.IsValid())
|
|
|
|
c.SupportSettings.ReportAProblemLink = NewPointer("invalid")
|
|
require.NotNil(t, c.IsValid())
|
|
|
|
c.SupportSettings.ReportAProblemLink = NewPointer("http://valid.com")
|
|
require.Nil(t, c.IsValid())
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestConfigEmptySiteName(t *testing.T) {
|
|
c1 := Config{
|
|
TeamSettings: TeamSettings{
|
|
SiteName: NewPointer(""),
|
|
},
|
|
}
|
|
c1.SetDefaults()
|
|
|
|
require.Equal(t, *c1.TeamSettings.SiteName, TeamSettingsDefaultSiteName)
|
|
}
|
|
|
|
func TestServiceSettingsIsValid(t *testing.T) {
|
|
for name, test := range map[string]struct {
|
|
ServiceSettings ServiceSettings
|
|
ExpectError bool
|
|
}{
|
|
"empty": {
|
|
ServiceSettings: ServiceSettings{},
|
|
ExpectError: false,
|
|
},
|
|
"OutgoingIntegrationRequestsTimeout is negative": {
|
|
ServiceSettings: ServiceSettings{
|
|
OutgoingIntegrationRequestsTimeout: NewPointer(int64(-1)),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
"OutgoingIntegrationRequestsTimeout is zero": {
|
|
ServiceSettings: ServiceSettings{
|
|
OutgoingIntegrationRequestsTimeout: NewPointer(int64(0)),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
"OutgoingIntegrationRequestsTimeout is positiv": {
|
|
ServiceSettings: ServiceSettings{
|
|
OutgoingIntegrationRequestsTimeout: NewPointer(int64(1)),
|
|
},
|
|
ExpectError: false,
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
test.ServiceSettings.SetDefaults(false)
|
|
|
|
appErr := test.ServiceSettings.isValid()
|
|
if test.ExpectError {
|
|
assert.NotNil(t, appErr)
|
|
} else {
|
|
assert.Nil(t, appErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfigEnableDeveloper(t *testing.T) {
|
|
testCases := []struct {
|
|
Description string
|
|
EnableDeveloper *bool
|
|
ExpectedSiteURL string
|
|
}{
|
|
{"enable developer is true", NewPointer(true), ServiceSettingsDefaultSiteURL},
|
|
{"enable developer is false", NewPointer(false), ""},
|
|
{"enable developer is nil", nil, ""},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.Description, func(t *testing.T) {
|
|
c1 := Config{
|
|
ServiceSettings: ServiceSettings{
|
|
EnableDeveloper: testCase.EnableDeveloper,
|
|
},
|
|
}
|
|
c1.SetDefaults()
|
|
|
|
require.Equal(t, testCase.ExpectedSiteURL, *c1.ServiceSettings.SiteURL)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfigDefaultFileSettingsDirectory(t *testing.T) {
|
|
c1 := Config{}
|
|
c1.SetDefaults()
|
|
|
|
require.Equal(t, *c1.FileSettings.Directory, "./data/")
|
|
}
|
|
|
|
func TestConfigDefaultEmailNotificationContentsType(t *testing.T) {
|
|
c1 := Config{}
|
|
c1.SetDefaults()
|
|
|
|
require.Equal(t, *c1.EmailSettings.EmailNotificationContentsType, EmailNotificationContentsFull)
|
|
}
|
|
|
|
func TestConfigDefaultFileSettingsS3SSE(t *testing.T) {
|
|
c1 := Config{}
|
|
c1.SetDefaults()
|
|
|
|
require.False(t, *c1.FileSettings.AmazonS3SSE)
|
|
}
|
|
|
|
func TestFileSettingsDirectoryWhitespaceValidation(t *testing.T) {
|
|
// Define Unicode whitespace characters to test
|
|
unicodeWhitespaces := []struct {
|
|
name string
|
|
char string
|
|
}{
|
|
{"Regular Space", " "},
|
|
{"Standard Space (U+0020)", "\u0020"},
|
|
{"No-Break Space (U+00A0)", "\u00A0"},
|
|
{"En Space (U+2002)", "\u2002"},
|
|
}
|
|
|
|
// Define all FileSettings path fields to test
|
|
pathSettings := []struct {
|
|
name string
|
|
validValue string
|
|
configSetter func(*Config, *string)
|
|
}{
|
|
{
|
|
"Directory",
|
|
"/path/to/directory",
|
|
func(cfg *Config, value *string) { cfg.FileSettings.Directory = value },
|
|
},
|
|
{
|
|
"AmazonS3PathPrefix",
|
|
"files/",
|
|
func(cfg *Config, value *string) { cfg.FileSettings.AmazonS3PathPrefix = value },
|
|
},
|
|
{
|
|
"ExportAmazonS3PathPrefix",
|
|
"exports/",
|
|
func(cfg *Config, value *string) { cfg.FileSettings.ExportAmazonS3PathPrefix = value },
|
|
},
|
|
{
|
|
"ExportDirectory",
|
|
"/path/to/exports",
|
|
func(cfg *Config, value *string) { cfg.FileSettings.ExportDirectory = value },
|
|
},
|
|
}
|
|
|
|
// Test valid paths first
|
|
for _, setting := range pathSettings {
|
|
t.Run(fmt.Sprintf("Valid %s", setting.name), func(t *testing.T) {
|
|
cfg := &Config{}
|
|
cfg.SetDefaults()
|
|
setting.configSetter(cfg, NewPointer(setting.validValue))
|
|
|
|
err := cfg.FileSettings.isValid()
|
|
require.Nil(t, err, "Expected no error but got: %v", err)
|
|
})
|
|
}
|
|
|
|
// Test path with space in the middle (should be valid)
|
|
t.Run("Directory with space in the middle (valid)", func(t *testing.T) {
|
|
cfg := &Config{}
|
|
cfg.SetDefaults()
|
|
cfg.FileSettings.Directory = NewPointer("/path/to/my directory")
|
|
|
|
err := cfg.FileSettings.isValid()
|
|
require.Nil(t, err, "Expected no error but got: %v", err)
|
|
})
|
|
|
|
// Test all combinations of settings, whitespace characters, and positions
|
|
for _, setting := range pathSettings {
|
|
for _, ws := range unicodeWhitespaces {
|
|
for _, position := range []string{"leading", "trailing"} {
|
|
t.Run(fmt.Sprintf("%s with %s %s whitespace", setting.name, position, ws.name), func(t *testing.T) {
|
|
cfg := &Config{}
|
|
cfg.SetDefaults()
|
|
|
|
var testValue string
|
|
if position == "leading" {
|
|
testValue = ws.char + setting.validValue
|
|
} else {
|
|
testValue = setting.validValue + ws.char
|
|
}
|
|
|
|
setting.configSetter(cfg, NewPointer(testValue))
|
|
|
|
err := cfg.FileSettings.isValid()
|
|
require.NotNil(t, err, "Expected an error but got none")
|
|
assert.Equal(t, "model.config.is_valid.directory_whitespace.app_error", err.Id)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConfigDefaultSignatureAlgorithm(t *testing.T) {
|
|
c1 := Config{}
|
|
c1.SetDefaults()
|
|
|
|
require.Equal(t, *c1.SamlSettings.SignatureAlgorithm, SamlSettingsDefaultSignatureAlgorithm)
|
|
require.Equal(t, *c1.SamlSettings.CanonicalAlgorithm, SamlSettingsDefaultCanonicalAlgorithm)
|
|
}
|
|
|
|
func TestConfigOverwriteSignatureAlgorithm(t *testing.T) {
|
|
const testAlgorithm = "FakeAlgorithm"
|
|
c1 := Config{
|
|
SamlSettings: SamlSettings{
|
|
CanonicalAlgorithm: NewPointer(testAlgorithm),
|
|
SignatureAlgorithm: NewPointer(testAlgorithm),
|
|
},
|
|
}
|
|
|
|
c1.SetDefaults()
|
|
|
|
require.Equal(t, *c1.SamlSettings.SignatureAlgorithm, testAlgorithm)
|
|
require.Equal(t, *c1.SamlSettings.CanonicalAlgorithm, testAlgorithm)
|
|
}
|
|
|
|
func TestWranglerSettingsIsValid(t *testing.T) {
|
|
// // Test valid domains
|
|
w := &WranglerSettings{
|
|
AllowedEmailDomain: []string{"example.com", "subdomain.example.com"},
|
|
}
|
|
if err := w.IsValid(); err != nil {
|
|
t.Errorf("Expected no error for valid domains, but got %v", err)
|
|
}
|
|
|
|
// Test invalid domains
|
|
w = &WranglerSettings{
|
|
AllowedEmailDomain: []string{"example", "example..com", "example-.com", "-example.com", "example.com.", "example.com-"},
|
|
}
|
|
if err := w.IsValid(); err == nil {
|
|
t.Errorf("Expected error for invalid domains, but got none")
|
|
}
|
|
}
|
|
|
|
func TestConfigIsValidDefaultAlgorithms(t *testing.T) {
|
|
c1 := Config{}
|
|
c1.SetDefaults()
|
|
|
|
*c1.SamlSettings.Enable = true
|
|
*c1.SamlSettings.Verify = false
|
|
*c1.SamlSettings.Encrypt = false
|
|
|
|
*c1.SamlSettings.IdpURL = "http://test.url.com"
|
|
*c1.SamlSettings.IdpDescriptorURL = "http://test.url.com"
|
|
*c1.SamlSettings.IdpCertificateFile = "certificatefile"
|
|
*c1.SamlSettings.ServiceProviderIdentifier = "http://test.url.com"
|
|
*c1.SamlSettings.EmailAttribute = "Email"
|
|
*c1.SamlSettings.UsernameAttribute = "Username"
|
|
|
|
appErr := c1.SamlSettings.isValid()
|
|
require.Nil(t, appErr)
|
|
}
|
|
|
|
func TestConfigServiceProviderDefault(t *testing.T) {
|
|
c1 := &Config{
|
|
SamlSettings: SamlSettings{
|
|
Enable: NewPointer(true),
|
|
Verify: NewPointer(false),
|
|
Encrypt: NewPointer(false),
|
|
IdpURL: NewPointer("http://test.url.com"),
|
|
IdpDescriptorURL: NewPointer("http://test2.url.com"),
|
|
IdpCertificateFile: NewPointer("certificatefile"),
|
|
EmailAttribute: NewPointer("Email"),
|
|
UsernameAttribute: NewPointer("Username"),
|
|
},
|
|
}
|
|
|
|
c1.SetDefaults()
|
|
assert.Equal(t, *c1.SamlSettings.ServiceProviderIdentifier, *c1.SamlSettings.IdpDescriptorURL)
|
|
|
|
appErr := c1.SamlSettings.isValid()
|
|
require.Nil(t, appErr)
|
|
}
|
|
|
|
func TestConfigIsValidFakeAlgorithm(t *testing.T) {
|
|
c1 := Config{}
|
|
c1.SetDefaults()
|
|
|
|
*c1.SamlSettings.Enable = true
|
|
*c1.SamlSettings.Verify = false
|
|
*c1.SamlSettings.Encrypt = false
|
|
|
|
*c1.SamlSettings.IdpURL = "http://test.url.com"
|
|
*c1.SamlSettings.IdpDescriptorURL = "http://test.url.com"
|
|
*c1.SamlSettings.IdpMetadataURL = "http://test.url.com"
|
|
*c1.SamlSettings.IdpCertificateFile = "certificatefile"
|
|
*c1.SamlSettings.ServiceProviderIdentifier = "http://test.url.com"
|
|
*c1.SamlSettings.EmailAttribute = "Email"
|
|
*c1.SamlSettings.UsernameAttribute = "Username"
|
|
|
|
temp := *c1.SamlSettings.CanonicalAlgorithm
|
|
*c1.SamlSettings.CanonicalAlgorithm = "Fake Algorithm"
|
|
appErr := c1.SamlSettings.isValid()
|
|
require.NotNil(t, appErr)
|
|
|
|
require.Equal(t, "model.config.is_valid.saml_canonical_algorithm.app_error", appErr.Message)
|
|
*c1.SamlSettings.CanonicalAlgorithm = temp
|
|
|
|
*c1.SamlSettings.SignatureAlgorithm = "Fake Algorithm"
|
|
appErr = c1.SamlSettings.isValid()
|
|
require.NotNil(t, appErr)
|
|
|
|
require.Equal(t, "model.config.is_valid.saml_signature_algorithm.app_error", appErr.Message)
|
|
}
|
|
|
|
func TestConfigOverwriteGuestSettings(t *testing.T) {
|
|
const attribute = "FakeAttributeName"
|
|
c1 := Config{
|
|
SamlSettings: SamlSettings{
|
|
GuestAttribute: NewPointer(attribute),
|
|
},
|
|
}
|
|
|
|
c1.SetDefaults()
|
|
|
|
require.Equal(t, *c1.SamlSettings.GuestAttribute, attribute)
|
|
}
|
|
|
|
func TestConfigOverwriteAdminSettings(t *testing.T) {
|
|
const attribute = "FakeAttributeName"
|
|
c1 := Config{
|
|
SamlSettings: SamlSettings{
|
|
AdminAttribute: NewPointer(attribute),
|
|
},
|
|
}
|
|
|
|
c1.SetDefaults()
|
|
|
|
require.Equal(t, *c1.SamlSettings.AdminAttribute, attribute)
|
|
}
|
|
|
|
func TestConfigDefaultServiceSettingsExperimentalGroupUnreadChannels(t *testing.T) {
|
|
c1 := Config{}
|
|
c1.SetDefaults()
|
|
|
|
require.Equal(t, *c1.ServiceSettings.ExperimentalGroupUnreadChannels, GroupUnreadChannelsDisabled)
|
|
|
|
// This setting was briefly a boolean, so ensure that those values still work as expected
|
|
c1 = Config{
|
|
ServiceSettings: ServiceSettings{
|
|
ExperimentalGroupUnreadChannels: NewPointer("1"),
|
|
},
|
|
}
|
|
c1.SetDefaults()
|
|
|
|
require.Equal(t, *c1.ServiceSettings.ExperimentalGroupUnreadChannels, GroupUnreadChannelsDefaultOn)
|
|
|
|
c1 = Config{
|
|
ServiceSettings: ServiceSettings{
|
|
ExperimentalGroupUnreadChannels: NewPointer("0"),
|
|
},
|
|
}
|
|
c1.SetDefaults()
|
|
|
|
require.Equal(t, *c1.ServiceSettings.ExperimentalGroupUnreadChannels, GroupUnreadChannelsDisabled)
|
|
}
|
|
|
|
func TestConfigDefaultNPSPluginState(t *testing.T) {
|
|
t.Run("should enable NPS plugin by default", func(t *testing.T) {
|
|
c1 := Config{}
|
|
c1.SetDefaults()
|
|
|
|
assert.True(t, c1.PluginSettings.PluginStates["com.mattermost.nps"].Enable)
|
|
})
|
|
|
|
t.Run("should enable NPS plugin if diagnostics are enabled", func(t *testing.T) {
|
|
c1 := Config{
|
|
LogSettings: LogSettings{
|
|
EnableDiagnostics: NewPointer(true),
|
|
},
|
|
}
|
|
|
|
c1.SetDefaults()
|
|
|
|
assert.True(t, c1.PluginSettings.PluginStates["com.mattermost.nps"].Enable)
|
|
})
|
|
|
|
t.Run("should not enable NPS plugin if diagnostics are disabled", func(t *testing.T) {
|
|
c1 := Config{
|
|
LogSettings: LogSettings{
|
|
EnableDiagnostics: NewPointer(false),
|
|
},
|
|
}
|
|
|
|
c1.SetDefaults()
|
|
|
|
assert.False(t, c1.PluginSettings.PluginStates["com.mattermost.nps"].Enable)
|
|
})
|
|
|
|
t.Run("should not re-enable NPS plugin after it has been disabled", func(t *testing.T) {
|
|
c1 := Config{
|
|
PluginSettings: PluginSettings{
|
|
PluginStates: map[string]*PluginState{
|
|
"com.mattermost.nps": {
|
|
Enable: false,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
c1.SetDefaults()
|
|
|
|
assert.False(t, c1.PluginSettings.PluginStates["com.mattermost.nps"].Enable)
|
|
})
|
|
}
|
|
|
|
func TestConfigDefaultChannelExportPluginState(t *testing.T) {
|
|
t.Run("should not enable ChannelExport plugin by default", func(t *testing.T) {
|
|
BuildEnterpriseReady = "true"
|
|
c1 := Config{}
|
|
c1.SetDefaults()
|
|
|
|
assert.Nil(t, c1.PluginSettings.PluginStates["com.mattermost.plugin-channel-export"])
|
|
})
|
|
}
|
|
|
|
func TestTeamSettingsIsValidSiteNameEmpty(t *testing.T) {
|
|
c1 := Config{}
|
|
c1.SetDefaults()
|
|
c1.TeamSettings.SiteName = NewPointer("")
|
|
|
|
// should not fail if ts.SiteName is not set, defaults are used
|
|
require.Nil(t, c1.TeamSettings.isValid())
|
|
}
|
|
|
|
func TestTeamSettingsDefaultJoinLeaveMessage(t *testing.T) {
|
|
c1 := Config{}
|
|
c1.SetDefaults()
|
|
|
|
// should default to true
|
|
require.Equal(t, NewPointer(true), c1.TeamSettings.EnableJoinLeaveMessageByDefault)
|
|
}
|
|
|
|
func TestMessageExportSettingsIsValidEnableExportNotSet(t *testing.T) {
|
|
mes := &MessageExportSettings{}
|
|
|
|
// should fail fast because mes.EnableExport is not set
|
|
require.NotNil(t, mes.isValid())
|
|
}
|
|
|
|
func TestMessageExportSettingsIsValidEnableExportFalse(t *testing.T) {
|
|
mes := &MessageExportSettings{
|
|
EnableExport: NewPointer(false),
|
|
}
|
|
|
|
// should fail fast because message export isn't enabled
|
|
require.Nil(t, mes.isValid())
|
|
}
|
|
|
|
func TestMessageExportSettingsIsValidExportFromTimestampInvalid(t *testing.T) {
|
|
mes := &MessageExportSettings{
|
|
EnableExport: NewPointer(true),
|
|
}
|
|
|
|
// should fail fast because export from timestamp isn't set
|
|
require.NotNil(t, mes.isValid())
|
|
|
|
mes.ExportFromTimestamp = NewPointer(int64(-1))
|
|
|
|
// should fail fast because export from timestamp isn't valid
|
|
require.NotNil(t, mes.isValid())
|
|
|
|
mes.ExportFromTimestamp = NewPointer(GetMillis() + 10000)
|
|
|
|
// should fail fast because export from timestamp is greater than current time
|
|
require.NotNil(t, mes.isValid())
|
|
}
|
|
|
|
func TestMessageExportSettingsIsValidDailyRunTimeInvalid(t *testing.T) {
|
|
mes := &MessageExportSettings{
|
|
EnableExport: NewPointer(true),
|
|
ExportFromTimestamp: NewPointer(int64(0)),
|
|
}
|
|
|
|
// should fail fast because daily runtime isn't set
|
|
require.NotNil(t, mes.isValid())
|
|
|
|
mes.DailyRunTime = NewPointer("33:33:33")
|
|
|
|
// should fail fast because daily runtime is invalid format
|
|
require.NotNil(t, mes.isValid())
|
|
}
|
|
|
|
func TestMessageExportSettingsIsValidBatchSizeInvalid(t *testing.T) {
|
|
mes := &MessageExportSettings{
|
|
EnableExport: NewPointer(true),
|
|
ExportFromTimestamp: NewPointer(int64(0)),
|
|
DailyRunTime: NewPointer("15:04"),
|
|
}
|
|
|
|
// should fail fast because batch size isn't set
|
|
require.NotNil(t, mes.isValid())
|
|
}
|
|
|
|
func TestMessageExportSettingsIsValidExportFormatInvalid(t *testing.T) {
|
|
mes := &MessageExportSettings{
|
|
EnableExport: NewPointer(true),
|
|
ExportFromTimestamp: NewPointer(int64(0)),
|
|
DailyRunTime: NewPointer("15:04"),
|
|
BatchSize: NewPointer(100),
|
|
}
|
|
|
|
// should fail fast because export format isn't set
|
|
require.NotNil(t, mes.isValid())
|
|
}
|
|
|
|
func TestMessageExportSettingsIsValidGlobalRelayEmailAddressInvalid(t *testing.T) {
|
|
mes := &MessageExportSettings{
|
|
EnableExport: NewPointer(true),
|
|
ExportFormat: NewPointer(ComplianceExportTypeGlobalrelay),
|
|
ExportFromTimestamp: NewPointer(int64(0)),
|
|
DailyRunTime: NewPointer("15:04"),
|
|
BatchSize: NewPointer(100),
|
|
}
|
|
|
|
// should fail fast because global relay email address isn't set
|
|
require.NotNil(t, mes.isValid())
|
|
}
|
|
|
|
func TestMessageExportSettingsIsValidActiance(t *testing.T) {
|
|
mes := &MessageExportSettings{
|
|
EnableExport: NewPointer(true),
|
|
ExportFormat: NewPointer(ComplianceExportTypeActiance),
|
|
ExportFromTimestamp: NewPointer(int64(0)),
|
|
DailyRunTime: NewPointer("15:04"),
|
|
BatchSize: NewPointer(100),
|
|
}
|
|
|
|
// should pass because everything is valid
|
|
require.Nil(t, mes.isValid())
|
|
}
|
|
|
|
func TestMessageExportSettingsIsValidGlobalRelaySettingsMissing(t *testing.T) {
|
|
mes := &MessageExportSettings{
|
|
EnableExport: NewPointer(true),
|
|
ExportFormat: NewPointer(ComplianceExportTypeGlobalrelay),
|
|
ExportFromTimestamp: NewPointer(int64(0)),
|
|
DailyRunTime: NewPointer("15:04"),
|
|
BatchSize: NewPointer(100),
|
|
}
|
|
|
|
// should fail because globalrelay settings are missing
|
|
require.NotNil(t, mes.isValid())
|
|
}
|
|
|
|
func TestMessageExportSettingsIsValidGlobalRelaySettingsInvalidCustomerType(t *testing.T) {
|
|
mes := &MessageExportSettings{
|
|
EnableExport: NewPointer(true),
|
|
ExportFormat: NewPointer(ComplianceExportTypeGlobalrelay),
|
|
ExportFromTimestamp: NewPointer(int64(0)),
|
|
DailyRunTime: NewPointer("15:04"),
|
|
BatchSize: NewPointer(100),
|
|
GlobalRelaySettings: &GlobalRelayMessageExportSettings{
|
|
CustomerType: NewPointer("Invalid"),
|
|
EmailAddress: NewPointer("valid@mattermost.com"),
|
|
SMTPUsername: NewPointer("SomeUsername"),
|
|
SMTPPassword: NewPointer("SomePassword"),
|
|
},
|
|
}
|
|
|
|
// should fail because customer type is invalid
|
|
require.NotNil(t, mes.isValid())
|
|
}
|
|
|
|
// func TestMessageExportSettingsIsValidGlobalRelaySettingsInvalidEmailAddress(t *testing.T) {
|
|
func TestMessageExportSettingsGlobalRelaySettings(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
value *GlobalRelayMessageExportSettings
|
|
success bool
|
|
}{
|
|
{
|
|
"Invalid email address",
|
|
&GlobalRelayMessageExportSettings{
|
|
CustomerType: NewPointer(GlobalrelayCustomerTypeA9),
|
|
EmailAddress: NewPointer("invalidEmailAddress"),
|
|
SMTPUsername: NewPointer("SomeUsername"),
|
|
SMTPPassword: NewPointer("SomePassword"),
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"Missing smtp username",
|
|
&GlobalRelayMessageExportSettings{
|
|
CustomerType: NewPointer(GlobalrelayCustomerTypeA10),
|
|
EmailAddress: NewPointer("valid@mattermost.com"),
|
|
SMTPPassword: NewPointer("SomePassword"),
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"Invalid smtp username",
|
|
&GlobalRelayMessageExportSettings{
|
|
CustomerType: NewPointer(GlobalrelayCustomerTypeA10),
|
|
EmailAddress: NewPointer("valid@mattermost.com"),
|
|
SMTPUsername: NewPointer(""),
|
|
SMTPPassword: NewPointer("SomePassword"),
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"Invalid smtp password",
|
|
&GlobalRelayMessageExportSettings{
|
|
CustomerType: NewPointer(GlobalrelayCustomerTypeA10),
|
|
EmailAddress: NewPointer("valid@mattermost.com"),
|
|
SMTPUsername: NewPointer("SomeUsername"),
|
|
SMTPPassword: NewPointer(""),
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"Valid data",
|
|
&GlobalRelayMessageExportSettings{
|
|
CustomerType: NewPointer(GlobalrelayCustomerTypeA9),
|
|
EmailAddress: NewPointer("valid@mattermost.com"),
|
|
SMTPUsername: NewPointer("SomeUsername"),
|
|
SMTPPassword: NewPointer("SomePassword"),
|
|
},
|
|
true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
mes := &MessageExportSettings{
|
|
EnableExport: NewPointer(true),
|
|
ExportFormat: NewPointer(ComplianceExportTypeGlobalrelay),
|
|
ExportFromTimestamp: NewPointer(int64(0)),
|
|
DailyRunTime: NewPointer("15:04"),
|
|
BatchSize: NewPointer(100),
|
|
GlobalRelaySettings: tt.value,
|
|
}
|
|
|
|
if tt.success {
|
|
require.Nil(t, mes.isValid())
|
|
} else {
|
|
require.NotNil(t, mes.isValid())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMessageExportSetDefaults(t *testing.T) {
|
|
mes := &MessageExportSettings{}
|
|
mes.SetDefaults()
|
|
|
|
require.False(t, *mes.EnableExport)
|
|
require.Equal(t, "01:00", *mes.DailyRunTime)
|
|
require.Equal(t, int64(0), *mes.ExportFromTimestamp)
|
|
require.Equal(t, 10000, *mes.BatchSize)
|
|
require.Equal(t, ComplianceExportTypeActiance, *mes.ExportFormat)
|
|
}
|
|
|
|
func TestMessageExportSetDefaultsExportEnabledExportFromTimestampNil(t *testing.T) {
|
|
// Test retained as protection against regression of MM-13185
|
|
mes := &MessageExportSettings{
|
|
EnableExport: NewPointer(true),
|
|
}
|
|
mes.SetDefaults()
|
|
|
|
require.True(t, *mes.EnableExport)
|
|
require.Equal(t, "01:00", *mes.DailyRunTime)
|
|
require.Equal(t, int64(0), *mes.ExportFromTimestamp)
|
|
require.True(t, *mes.ExportFromTimestamp <= GetMillis())
|
|
require.Equal(t, 10000, *mes.BatchSize)
|
|
}
|
|
|
|
func TestMessageExportSetDefaultsExportEnabledExportFromTimestampZero(t *testing.T) {
|
|
// Test retained as protection against regression of MM-13185
|
|
mes := &MessageExportSettings{
|
|
EnableExport: NewPointer(true),
|
|
ExportFromTimestamp: NewPointer(int64(0)),
|
|
}
|
|
mes.SetDefaults()
|
|
|
|
require.True(t, *mes.EnableExport)
|
|
require.Equal(t, "01:00", *mes.DailyRunTime)
|
|
require.Equal(t, int64(0), *mes.ExportFromTimestamp)
|
|
require.True(t, *mes.ExportFromTimestamp <= GetMillis())
|
|
require.Equal(t, 10000, *mes.BatchSize)
|
|
}
|
|
|
|
func TestMessageExportSetDefaultsExportEnabledExportFromTimestampNonZero(t *testing.T) {
|
|
mes := &MessageExportSettings{
|
|
EnableExport: NewPointer(true),
|
|
ExportFromTimestamp: NewPointer(int64(12345)),
|
|
}
|
|
mes.SetDefaults()
|
|
|
|
require.True(t, *mes.EnableExport)
|
|
require.Equal(t, "01:00", *mes.DailyRunTime)
|
|
require.Equal(t, int64(12345), *mes.ExportFromTimestamp)
|
|
require.Equal(t, 10000, *mes.BatchSize)
|
|
}
|
|
|
|
func TestMessageExportSetDefaultsExportDisabledExportFromTimestampNil(t *testing.T) {
|
|
mes := &MessageExportSettings{
|
|
EnableExport: NewPointer(false),
|
|
}
|
|
mes.SetDefaults()
|
|
|
|
require.False(t, *mes.EnableExport)
|
|
require.Equal(t, "01:00", *mes.DailyRunTime)
|
|
require.Equal(t, int64(0), *mes.ExportFromTimestamp)
|
|
require.Equal(t, 10000, *mes.BatchSize)
|
|
}
|
|
|
|
func TestMessageExportSetDefaultsExportDisabledExportFromTimestampZero(t *testing.T) {
|
|
mes := &MessageExportSettings{
|
|
EnableExport: NewPointer(false),
|
|
ExportFromTimestamp: NewPointer(int64(0)),
|
|
}
|
|
mes.SetDefaults()
|
|
|
|
require.False(t, *mes.EnableExport)
|
|
require.Equal(t, "01:00", *mes.DailyRunTime)
|
|
require.Equal(t, int64(0), *mes.ExportFromTimestamp)
|
|
require.Equal(t, 10000, *mes.BatchSize)
|
|
}
|
|
|
|
func TestMessageExportSetDefaultsExportDisabledExportFromTimestampNonZero(t *testing.T) {
|
|
// Test retained as protection against regression of MM-13185
|
|
mes := &MessageExportSettings{
|
|
EnableExport: NewPointer(false),
|
|
ExportFromTimestamp: NewPointer(int64(12345)),
|
|
}
|
|
mes.SetDefaults()
|
|
|
|
require.False(t, *mes.EnableExport)
|
|
require.Equal(t, "01:00", *mes.DailyRunTime)
|
|
require.Equal(t, int64(12345), *mes.ExportFromTimestamp)
|
|
require.Equal(t, 10000, *mes.BatchSize)
|
|
}
|
|
|
|
func TestDisplaySettingsIsValidCustomURLSchemes(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
value []string
|
|
valid bool
|
|
}{
|
|
{
|
|
name: "empty",
|
|
value: []string{},
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "custom protocol",
|
|
value: []string{"steam"},
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "multiple custom protocols",
|
|
value: []string{"bitcoin", "rss", "redis"},
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "containing numbers",
|
|
value: []string{"ut2004", "ts3server", "h323"},
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "containing period",
|
|
value: []string{"iris.beep"},
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "containing hyphen",
|
|
value: []string{"ms-excel"},
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "containing plus",
|
|
value: []string{"coap+tcp", "coap+ws"},
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "starting with number",
|
|
value: []string{"4four"},
|
|
valid: false,
|
|
},
|
|
{
|
|
name: "starting with period",
|
|
value: []string{"data", ".dot"},
|
|
valid: false,
|
|
},
|
|
{
|
|
name: "starting with hyphen",
|
|
value: []string{"-hyphen", "dns"},
|
|
valid: false,
|
|
},
|
|
{
|
|
name: "invalid symbols",
|
|
value: []string{"!!fun!!"},
|
|
valid: false,
|
|
},
|
|
{
|
|
name: "invalid letters",
|
|
value: []string{"école"},
|
|
valid: false,
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
ds := &DisplaySettings{}
|
|
ds.SetDefaults()
|
|
|
|
ds.CustomURLSchemes = test.value
|
|
|
|
if appErr := ds.isValid(); appErr != nil && test.valid {
|
|
t.Error("Expected CustomURLSchemes to be valid but got error:", appErr)
|
|
} else if appErr == nil && !test.valid {
|
|
t.Error("Expected CustomURLSchemes to be invalid but got no error")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListenAddressIsValidated(t *testing.T) {
|
|
testValues := map[string]bool{
|
|
":8065": true,
|
|
":9917": true,
|
|
"0.0.0.0:9917": true,
|
|
"[2001:db8::68]:9918": true,
|
|
"[::1]:8065": true,
|
|
"localhost:8065": true,
|
|
"test.com:8065": true,
|
|
":0": true,
|
|
":33147": true,
|
|
"123:8065": false,
|
|
"[::1]:99999": false,
|
|
"[::1]:-1": false,
|
|
"[::1]:8065a": false,
|
|
"0.0.0:9917": false,
|
|
"0.0.0.0:9917/": false,
|
|
"0..0.0:9917/": false,
|
|
"0.0.0222.0:9917/": false,
|
|
"http://0.0.0.0:9917/": false,
|
|
"http://0.0.0.0:9917": false,
|
|
"8065": false,
|
|
"[2001:db8::68]": false,
|
|
}
|
|
|
|
for key, expected := range testValues {
|
|
ss := &ServiceSettings{
|
|
ListenAddress: NewPointer(key),
|
|
}
|
|
ss.SetDefaults(true)
|
|
if expected {
|
|
require.Nil(t, ss.isValid(), fmt.Sprintf("Got an error from '%v'.", key))
|
|
} else {
|
|
appErr := ss.isValid()
|
|
require.NotNil(t, appErr, fmt.Sprintf("Expected '%v' to throw an error.", key))
|
|
require.Equal(t, "model.config.is_valid.listen_address.app_error", appErr.Message)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestImageProxySettingsSetDefaults(t *testing.T) {
|
|
t.Run("default settings", func(t *testing.T) {
|
|
ips := ImageProxySettings{}
|
|
ips.SetDefaults()
|
|
|
|
assert.Equal(t, false, *ips.Enable)
|
|
assert.Equal(t, ImageProxyTypeLocal, *ips.ImageProxyType)
|
|
assert.Equal(t, "", *ips.RemoteImageProxyURL)
|
|
assert.Equal(t, "", *ips.RemoteImageProxyOptions)
|
|
})
|
|
}
|
|
|
|
func TestImageProxySettingsIsValid(t *testing.T) {
|
|
for _, test := range []struct {
|
|
Name string
|
|
Enable bool
|
|
ImageProxyType string
|
|
RemoteImageProxyURL string
|
|
RemoteImageProxyOptions string
|
|
ExpectError bool
|
|
}{
|
|
{
|
|
Name: "disabled",
|
|
Enable: false,
|
|
ExpectError: false,
|
|
},
|
|
{
|
|
Name: "disabled with bad values",
|
|
Enable: false,
|
|
ImageProxyType: "garbage",
|
|
RemoteImageProxyURL: "garbage",
|
|
RemoteImageProxyOptions: "garbage",
|
|
ExpectError: false,
|
|
},
|
|
{
|
|
Name: "missing type",
|
|
Enable: true,
|
|
ImageProxyType: "",
|
|
ExpectError: true,
|
|
},
|
|
{
|
|
Name: "local",
|
|
Enable: true,
|
|
ImageProxyType: "local",
|
|
RemoteImageProxyURL: "garbage",
|
|
RemoteImageProxyOptions: "garbage",
|
|
ExpectError: false,
|
|
},
|
|
{
|
|
Name: "atmos/camo",
|
|
Enable: true,
|
|
ImageProxyType: ImageProxyTypeAtmosCamo,
|
|
RemoteImageProxyURL: "someurl",
|
|
RemoteImageProxyOptions: "someoptions",
|
|
ExpectError: false,
|
|
},
|
|
{
|
|
Name: "atmos/camo, missing url",
|
|
Enable: true,
|
|
ImageProxyType: ImageProxyTypeAtmosCamo,
|
|
RemoteImageProxyURL: "",
|
|
RemoteImageProxyOptions: "garbage",
|
|
ExpectError: true,
|
|
},
|
|
{
|
|
Name: "atmos/camo, missing options",
|
|
Enable: true,
|
|
ImageProxyType: ImageProxyTypeAtmosCamo,
|
|
RemoteImageProxyURL: "someurl",
|
|
RemoteImageProxyOptions: "",
|
|
ExpectError: true,
|
|
},
|
|
} {
|
|
t.Run(test.Name, func(t *testing.T) {
|
|
ips := &ImageProxySettings{
|
|
Enable: &test.Enable,
|
|
ImageProxyType: &test.ImageProxyType,
|
|
RemoteImageProxyURL: &test.RemoteImageProxyURL,
|
|
RemoteImageProxyOptions: &test.RemoteImageProxyOptions,
|
|
}
|
|
|
|
appErr := ips.isValid()
|
|
if test.ExpectError {
|
|
assert.NotNil(t, appErr)
|
|
} else {
|
|
assert.Nil(t, appErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLdapSettingsIsValid(t *testing.T) {
|
|
for _, test := range []struct {
|
|
Name string
|
|
LdapSettings LdapSettings
|
|
ExpectError bool
|
|
}{
|
|
{
|
|
Name: "disabled",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(false),
|
|
},
|
|
ExpectError: false,
|
|
},
|
|
{
|
|
Name: "missing server",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer(""),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
UserFilter: NewPointer(""),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
{
|
|
Name: "empty user filter",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
UserFilter: NewPointer(""),
|
|
},
|
|
ExpectError: false,
|
|
},
|
|
{
|
|
Name: "valid user filter #1",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
UserFilter: NewPointer("(property=value)"),
|
|
},
|
|
ExpectError: false,
|
|
},
|
|
{
|
|
Name: "invalid user filter #1",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
UserFilter: NewPointer("("),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
{
|
|
Name: "invalid user filter #2",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
UserFilter: NewPointer("()"),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
{
|
|
Name: "valid user filter #2",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
UserFilter: NewPointer("(&(property=value)(otherthing=othervalue))"),
|
|
},
|
|
ExpectError: false,
|
|
},
|
|
{
|
|
Name: "valid user filter #3",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
UserFilter: NewPointer("(&(property=value)(|(otherthing=othervalue)(other=thing)))"),
|
|
},
|
|
ExpectError: false,
|
|
},
|
|
{
|
|
Name: "invalid user filter #3",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
UserFilter: NewPointer("(&(property=value)(|(otherthing=othervalue)(other=thing))"),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
{
|
|
Name: "invalid user filter #4",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
UserFilter: NewPointer("(&(property=value)((otherthing=othervalue)(other=thing)))"),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
|
|
{
|
|
Name: "valid guest filter #1",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
GuestFilter: NewPointer("(property=value)"),
|
|
},
|
|
ExpectError: false,
|
|
},
|
|
{
|
|
Name: "invalid guest filter #1",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
GuestFilter: NewPointer("("),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
{
|
|
Name: "invalid guest filter #2",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
GuestFilter: NewPointer("()"),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
{
|
|
Name: "valid guest filter #2",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
GuestFilter: NewPointer("(&(property=value)(otherthing=othervalue))"),
|
|
},
|
|
ExpectError: false,
|
|
},
|
|
{
|
|
Name: "valid guest filter #3",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
GuestFilter: NewPointer("(&(property=value)(|(otherthing=othervalue)(other=thing)))"),
|
|
},
|
|
ExpectError: false,
|
|
},
|
|
{
|
|
Name: "invalid guest filter #3",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
GuestFilter: NewPointer("(&(property=value)(|(otherthing=othervalue)(other=thing))"),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
{
|
|
Name: "invalid guest filter #4",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
GuestFilter: NewPointer("(&(property=value)((otherthing=othervalue)(other=thing)))"),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
|
|
{
|
|
Name: "valid Admin filter #1",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
AdminFilter: NewPointer("(property=value)"),
|
|
},
|
|
ExpectError: false,
|
|
},
|
|
{
|
|
Name: "invalid Admin filter #1",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
AdminFilter: NewPointer("("),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
{
|
|
Name: "invalid Admin filter #2",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
AdminFilter: NewPointer("()"),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
{
|
|
Name: "valid Admin filter #2",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
AdminFilter: NewPointer("(&(property=value)(otherthing=othervalue))"),
|
|
},
|
|
ExpectError: false,
|
|
},
|
|
{
|
|
Name: "valid Admin filter #3",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
AdminFilter: NewPointer("(&(property=value)(|(otherthing=othervalue)(other=thing)))"),
|
|
},
|
|
ExpectError: false,
|
|
},
|
|
{
|
|
Name: "invalid Admin filter #3",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
AdminFilter: NewPointer("(&(property=value)(|(otherthing=othervalue)(other=thing))"),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
{
|
|
Name: "invalid Admin filter #4",
|
|
LdapSettings: LdapSettings{
|
|
Enable: NewPointer(true),
|
|
LdapServer: NewPointer("server"),
|
|
BaseDN: NewPointer("basedn"),
|
|
EmailAttribute: NewPointer("email"),
|
|
UsernameAttribute: NewPointer("username"),
|
|
IdAttribute: NewPointer("id"),
|
|
LoginIdAttribute: NewPointer("loginid"),
|
|
AdminFilter: NewPointer("(&(property=value)((otherthing=othervalue)(other=thing)))"),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
} {
|
|
t.Run(test.Name, func(t *testing.T) {
|
|
test.LdapSettings.SetDefaults()
|
|
|
|
appErr := test.LdapSettings.isValid()
|
|
if test.ExpectError {
|
|
assert.NotNil(t, appErr)
|
|
} else {
|
|
assert.Nil(t, appErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLogSettingsIsValid(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for name, test := range map[string]struct {
|
|
LogSettings LogSettings
|
|
ExpectError bool
|
|
}{
|
|
"empty": {
|
|
LogSettings: LogSettings{},
|
|
ExpectError: false,
|
|
},
|
|
"AdvancedLoggingJSON contains empty string": {
|
|
LogSettings: LogSettings{
|
|
AdvancedLoggingJSON: json.RawMessage(``),
|
|
},
|
|
ExpectError: false,
|
|
},
|
|
"AdvancedLoggingJSON contains empty JSON": {
|
|
LogSettings: LogSettings{
|
|
AdvancedLoggingJSON: json.RawMessage(`{}`),
|
|
},
|
|
ExpectError: false,
|
|
},
|
|
"AdvancedLoggingJSON has JSON error ": {
|
|
LogSettings: LogSettings{
|
|
AdvancedLoggingJSON: json.RawMessage(`
|
|
{
|
|
"foo": "bar",
|
|
`),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
"AdvancedLoggingJSON has missing target": {
|
|
LogSettings: LogSettings{
|
|
AdvancedLoggingJSON: json.RawMessage(`
|
|
{
|
|
"foo": "bar",
|
|
}
|
|
`),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
"AdvancedLoggingJSON has an unknown Type": {
|
|
LogSettings: LogSettings{
|
|
AdvancedLoggingJSON: json.RawMessage(`
|
|
{
|
|
"console-log": {
|
|
"Type": "XYZ",
|
|
"Format": "json",
|
|
"Levels": [
|
|
{"ID": 10, "Name": "stdlog", "Stacktrace": false},
|
|
{"ID": 5, "Name": "debug", "Stacktrace": false},
|
|
{"ID": 4, "Name": "info", "Stacktrace": false, "color": 36},
|
|
{"ID": 3, "Name": "warn", "Stacktrace": false, "color": 33},
|
|
{"ID": 2, "Name": "error", "Stacktrace": true, "color": 31},
|
|
{"ID": 1, "Name": "fatal", "Stacktrace": true},
|
|
{"ID": 0, "Name": "panic", "Stacktrace": true}
|
|
],
|
|
"Options": {
|
|
"Out": "stdout"
|
|
},
|
|
"MaxQueueSize": 1000
|
|
}
|
|
}
|
|
`),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
"AdvancedLoggingJSON is valid": {
|
|
LogSettings: LogSettings{
|
|
AdvancedLoggingJSON: json.RawMessage(`
|
|
{
|
|
"console-log": {
|
|
"Type": "console",
|
|
"Format": "json",
|
|
"Levels": [
|
|
{"ID": 5, "Name": "debug", "Stacktrace": false},
|
|
{"ID": 4, "Name": "info", "Stacktrace": false, "color": 36},
|
|
{"ID": 3, "Name": "warn", "Stacktrace": false, "color": 33},
|
|
{"ID": 2, "Name": "error", "Stacktrace": true, "color": 31}
|
|
],
|
|
"Options": {
|
|
"Out": "stdout"
|
|
},
|
|
"MaxQueueSize": 1000
|
|
}
|
|
}
|
|
`),
|
|
},
|
|
ExpectError: false,
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
test.LogSettings.SetDefaults()
|
|
|
|
appErr := test.LogSettings.isValid()
|
|
if test.ExpectError {
|
|
assert.NotNil(t, appErr)
|
|
} else {
|
|
assert.Nil(t, appErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfigSanitize(t *testing.T) {
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
|
|
*c.LdapSettings.BindPassword = "foo"
|
|
*c.FileSettings.AmazonS3SecretAccessKey = "bar"
|
|
*c.EmailSettings.SMTPPassword = "baz"
|
|
*c.GitLabSettings.Secret = "bingo"
|
|
*c.OpenIdSettings.Secret = "secret"
|
|
c.SqlSettings.DataSourceReplicas = []string{"stuff"}
|
|
c.SqlSettings.DataSourceSearchReplicas = []string{"stuff"}
|
|
c.SqlSettings.ReplicaLagSettings = []*ReplicaLagSettings{{
|
|
DataSource: NewPointer("DataSource"),
|
|
QueryAbsoluteLag: NewPointer("QueryAbsoluteLag"),
|
|
QueryTimeLag: NewPointer("QueryTimeLag"),
|
|
}}
|
|
|
|
c.Sanitize(nil, nil)
|
|
|
|
assert.Equal(t, FakeSetting, *c.LdapSettings.BindPassword)
|
|
assert.Equal(t, FakeSetting, *c.FileSettings.PublicLinkSalt)
|
|
assert.Equal(t, FakeSetting, *c.FileSettings.AmazonS3SecretAccessKey)
|
|
assert.Equal(t, FakeSetting, *c.EmailSettings.SMTPPassword)
|
|
assert.Equal(t, FakeSetting, *c.GitLabSettings.Secret)
|
|
assert.Equal(t, FakeSetting, *c.OpenIdSettings.Secret)
|
|
assert.Equal(t, FakeSetting, *c.SqlSettings.DataSource)
|
|
assert.Equal(t, FakeSetting, *c.SqlSettings.AtRestEncryptKey)
|
|
assert.Equal(t, FakeSetting, *c.ElasticsearchSettings.Password)
|
|
assert.Equal(t, FakeSetting, c.SqlSettings.DataSourceReplicas[0])
|
|
assert.Equal(t, FakeSetting, c.SqlSettings.DataSourceSearchReplicas[0])
|
|
|
|
require.Len(t, c.SqlSettings.ReplicaLagSettings, 1)
|
|
assert.Equal(t, FakeSetting, *c.SqlSettings.ReplicaLagSettings[0].DataSource)
|
|
assert.Equal(t, "QueryAbsoluteLag", *c.SqlSettings.ReplicaLagSettings[0].QueryAbsoluteLag)
|
|
assert.Equal(t, "QueryTimeLag", *c.SqlSettings.ReplicaLagSettings[0].QueryTimeLag)
|
|
|
|
t.Run("with default config", func(t *testing.T) {
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
c.Sanitize(nil, nil)
|
|
|
|
assert.Len(t, c.SqlSettings.ReplicaLagSettings, 0)
|
|
})
|
|
|
|
t.Run("partially sanitize DataSource", func(t *testing.T) {
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
*c.SqlSettings.DataSource = "postgres://mmuser:mostest@localhost:5432/mattermost_test?sslmode=disable"
|
|
c.Sanitize(nil, &SanitizeOptions{PartiallyRedactDataSources: true})
|
|
|
|
expectedURL := "postgres://" + SanitizedPassword + ":" + SanitizedPassword + "@localhost:5432/mattermost_test?sslmode=disable"
|
|
assert.Equal(t, expectedURL, *c.SqlSettings.DataSource)
|
|
})
|
|
}
|
|
|
|
func TestPluginSettingsSanitize(t *testing.T) {
|
|
const (
|
|
pluginID1 = "plugin.id"
|
|
pluginID2 = "another.plugin"
|
|
)
|
|
settingsPlugin1 := map[string]any{
|
|
"someoldsettings": "some old value",
|
|
"somesetting": "some value",
|
|
"secrettext": "a secret",
|
|
"secretnumber": 123,
|
|
}
|
|
|
|
settingsPlugin2 := map[string]any{
|
|
"somesetting": 456,
|
|
}
|
|
|
|
for name, tc := range map[string]struct {
|
|
manifests []*Manifest
|
|
expected map[string]map[string]any
|
|
}{
|
|
"nil list of manifests": {
|
|
manifests: nil,
|
|
expected: map[string]map[string]any{},
|
|
},
|
|
"empty list of manifests": {
|
|
manifests: []*Manifest{},
|
|
expected: map[string]map[string]any{},
|
|
},
|
|
"one plugin installed without settings schema": {
|
|
manifests: []*Manifest{
|
|
{
|
|
Id: pluginID1,
|
|
SettingsSchema: nil,
|
|
},
|
|
},
|
|
expected: map[string]map[string]any{
|
|
pluginID1: {
|
|
"someoldsettings": "some old value",
|
|
"somesetting": "some value",
|
|
"secrettext": "a secret",
|
|
"secretnumber": 123,
|
|
},
|
|
},
|
|
},
|
|
"one plugin installed empty settings schema": {
|
|
manifests: []*Manifest{
|
|
{
|
|
Id: pluginID1,
|
|
SettingsSchema: &PluginSettingsSchema{},
|
|
},
|
|
},
|
|
expected: map[string]map[string]any{
|
|
pluginID1: {
|
|
"someoldsettings": "some old value",
|
|
"somesetting": "some value",
|
|
"secrettext": "a secret",
|
|
"secretnumber": 123,
|
|
},
|
|
},
|
|
},
|
|
"one plugin installed empty settings list": {
|
|
manifests: []*Manifest{
|
|
{
|
|
Id: pluginID1,
|
|
SettingsSchema: &PluginSettingsSchema{
|
|
Settings: []*PluginSetting{},
|
|
},
|
|
},
|
|
},
|
|
expected: map[string]map[string]any{
|
|
pluginID1: {
|
|
"someoldsettings": "some old value",
|
|
"somesetting": "some value",
|
|
"secrettext": "a secret",
|
|
"secretnumber": 123,
|
|
},
|
|
},
|
|
},
|
|
"one plugin installed": {
|
|
manifests: []*Manifest{
|
|
{
|
|
Id: pluginID1,
|
|
SettingsSchema: &PluginSettingsSchema{
|
|
Settings: []*PluginSetting{
|
|
{
|
|
Key: "somesetting",
|
|
Type: "text",
|
|
Secret: false,
|
|
},
|
|
{
|
|
Key: "secrettext",
|
|
Type: "text",
|
|
Secret: true,
|
|
},
|
|
{
|
|
Key: "secretnumber",
|
|
Type: "number",
|
|
Secret: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expected: map[string]map[string]any{
|
|
pluginID1: {
|
|
"someoldsettings": "some old value",
|
|
"somesetting": "some value",
|
|
"secrettext": FakeSetting,
|
|
"secretnumber": FakeSetting,
|
|
},
|
|
},
|
|
},
|
|
"two plugins installed": {
|
|
manifests: []*Manifest{
|
|
{
|
|
Id: pluginID1,
|
|
SettingsSchema: &PluginSettingsSchema{
|
|
Settings: []*PluginSetting{
|
|
{
|
|
Key: "somesetting",
|
|
Type: "text",
|
|
Secret: false,
|
|
},
|
|
{
|
|
Key: "secrettext",
|
|
Type: "text",
|
|
Secret: true,
|
|
},
|
|
{
|
|
Key: "secretnumber",
|
|
Type: "number",
|
|
Secret: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Id: pluginID2,
|
|
SettingsSchema: &PluginSettingsSchema{
|
|
Settings: []*PluginSetting{
|
|
{
|
|
Key: "somesetting",
|
|
Type: "number",
|
|
Secret: false,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expected: map[string]map[string]any{
|
|
pluginID1: {
|
|
"someoldsettings": "some old value",
|
|
"somesetting": "some value",
|
|
"secrettext": FakeSetting,
|
|
"secretnumber": FakeSetting,
|
|
},
|
|
pluginID2: {
|
|
"somesetting": 456,
|
|
},
|
|
},
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
c := PluginSettings{}
|
|
c.SetDefaults(*NewLogSettings())
|
|
|
|
c.Plugins[pluginID1] = make(map[string]any)
|
|
maps.Copy(c.Plugins[pluginID1], settingsPlugin1)
|
|
c.Plugins[pluginID2] = make(map[string]any)
|
|
maps.Copy(c.Plugins[pluginID2], settingsPlugin2)
|
|
|
|
c.Sanitize(tc.manifests)
|
|
|
|
assert.Equal(t, tc.expected, c.Plugins, name)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSanitizeDataSource(t *testing.T) {
|
|
t.Run(DatabaseDriverPostgres, func(t *testing.T) {
|
|
testCases := []struct {
|
|
Original string
|
|
Sanitized string
|
|
}{
|
|
{
|
|
"",
|
|
"",
|
|
},
|
|
{
|
|
"postgres://mmuser:mostest@localhost",
|
|
"postgres://" + SanitizedPassword + ":" + SanitizedPassword + "@localhost",
|
|
},
|
|
{
|
|
"postgres://mmuser:mostest@localhost/dummy?sslmode=disable",
|
|
"postgres://" + SanitizedPassword + ":" + SanitizedPassword + "@localhost/dummy?sslmode=disable",
|
|
},
|
|
{
|
|
"postgres://localhost/dummy?sslmode=disable&user=mmuser&password=mostest",
|
|
"postgres://" + SanitizedPassword + ":" + SanitizedPassword + "@localhost/dummy?sslmode=disable",
|
|
},
|
|
}
|
|
driver := DatabaseDriverPostgres
|
|
for _, tc := range testCases {
|
|
out, err := SanitizeDataSource(driver, tc.Original)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tc.Sanitized, out)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestConfigFilteredByTag(t *testing.T) {
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
|
|
cfgMap := configToMapFilteredByTag(c, ConfigAccessTagType, ConfigAccessTagCloudRestrictable)
|
|
|
|
// Remove entire sections but the map is still there
|
|
clusterSettings, ok := cfgMap["SqlSettings"].(map[string]any)
|
|
require.True(t, ok)
|
|
require.Empty(t, clusterSettings)
|
|
|
|
// Some fields are removed if they have the filtering tag
|
|
serviceSettings, ok := cfgMap["ServiceSettings"].(map[string]any)
|
|
require.True(t, ok)
|
|
_, ok = serviceSettings["ListenAddress"]
|
|
require.False(t, ok)
|
|
}
|
|
|
|
func TestConfigToJSONFiltered(t *testing.T) {
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
|
|
jsonCfgFiltered, err := c.ToJSONFiltered(ConfigAccessTagType, ConfigAccessTagCloudRestrictable)
|
|
require.NoError(t, err)
|
|
|
|
unmarshaledCfg := make(map[string]json.RawMessage)
|
|
err = json.Unmarshal(jsonCfgFiltered, &unmarshaledCfg)
|
|
require.NoError(t, err)
|
|
|
|
_, ok := unmarshaledCfg["SqlSettings"]
|
|
require.False(t, ok)
|
|
|
|
serviceSettingsRaw, ok := unmarshaledCfg["ServiceSettings"]
|
|
require.True(t, ok)
|
|
|
|
unmarshaledServiceSettings := make(map[string]json.RawMessage)
|
|
err = json.Unmarshal([]byte(serviceSettingsRaw), &unmarshaledServiceSettings)
|
|
require.NoError(t, err)
|
|
|
|
_, ok = unmarshaledServiceSettings["ListenAddress"]
|
|
require.False(t, ok)
|
|
_, ok = unmarshaledServiceSettings["SiteURL"]
|
|
require.True(t, ok)
|
|
}
|
|
|
|
func TestConfigMarketplaceDefaults(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("no marketplace url", func(t *testing.T) {
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
|
|
require.True(t, *c.PluginSettings.EnableMarketplace)
|
|
require.Equal(t, PluginSettingsDefaultMarketplaceURL, *c.PluginSettings.MarketplaceURL)
|
|
})
|
|
|
|
t.Run("old marketplace url", func(t *testing.T) {
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
|
|
*c.PluginSettings.MarketplaceURL = PluginSettingsOldMarketplaceURL
|
|
c.SetDefaults()
|
|
|
|
require.True(t, *c.PluginSettings.EnableMarketplace)
|
|
require.Equal(t, PluginSettingsDefaultMarketplaceURL, *c.PluginSettings.MarketplaceURL)
|
|
})
|
|
|
|
t.Run("custom marketplace url", func(t *testing.T) {
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
|
|
*c.PluginSettings.MarketplaceURL = "https://marketplace.example.com"
|
|
c.SetDefaults()
|
|
|
|
require.True(t, *c.PluginSettings.EnableMarketplace)
|
|
require.Equal(t, "https://marketplace.example.com", *c.PluginSettings.MarketplaceURL)
|
|
})
|
|
}
|
|
|
|
func TestSetDefaultFeatureFlagBehaviour(t *testing.T) {
|
|
cfg := Config{}
|
|
cfg.SetDefaults()
|
|
|
|
require.NotNil(t, cfg.FeatureFlags)
|
|
require.Equal(t, "off", cfg.FeatureFlags.TestFeature)
|
|
|
|
cfg = Config{
|
|
FeatureFlags: &FeatureFlags{
|
|
TestFeature: "somevalue",
|
|
},
|
|
}
|
|
cfg.SetDefaults()
|
|
require.NotNil(t, cfg.FeatureFlags)
|
|
require.Equal(t, "somevalue", cfg.FeatureFlags.TestFeature)
|
|
}
|
|
|
|
func TestConfigImportSettingsDefaults(t *testing.T) {
|
|
cfg := Config{}
|
|
cfg.SetDefaults()
|
|
|
|
require.Equal(t, "./import", *cfg.ImportSettings.Directory)
|
|
require.Equal(t, 30, *cfg.ImportSettings.RetentionDays)
|
|
}
|
|
|
|
func TestConfigImportSettingsIsValid(t *testing.T) {
|
|
cfg := Config{}
|
|
cfg.SetDefaults()
|
|
|
|
appErr := cfg.ImportSettings.isValid()
|
|
require.Nil(t, appErr)
|
|
|
|
*cfg.ImportSettings.Directory = ""
|
|
appErr = cfg.ImportSettings.isValid()
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, "model.config.is_valid.import.directory.app_error", appErr.Id)
|
|
|
|
cfg.SetDefaults()
|
|
|
|
*cfg.ImportSettings.RetentionDays = 0
|
|
appErr = cfg.ImportSettings.isValid()
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, "model.config.is_valid.import.retention_days_too_low.app_error", appErr.Id)
|
|
}
|
|
|
|
func TestConfigExportSettingsDefaults(t *testing.T) {
|
|
cfg := Config{}
|
|
cfg.SetDefaults()
|
|
|
|
require.Equal(t, "./export", *cfg.ExportSettings.Directory)
|
|
require.Equal(t, 30, *cfg.ExportSettings.RetentionDays)
|
|
}
|
|
|
|
func TestConfigExportSettingsIsValid(t *testing.T) {
|
|
cfg := Config{}
|
|
cfg.SetDefaults()
|
|
|
|
appErr := cfg.ExportSettings.isValid()
|
|
require.Nil(t, appErr)
|
|
|
|
*cfg.ExportSettings.Directory = ""
|
|
appErr = cfg.ExportSettings.isValid()
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, "model.config.is_valid.export.directory.app_error", appErr.Id)
|
|
|
|
cfg.SetDefaults()
|
|
|
|
*cfg.ExportSettings.RetentionDays = 0
|
|
appErr = cfg.ExportSettings.isValid()
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, "model.config.is_valid.export.retention_days_too_low.app_error", appErr.Id)
|
|
}
|
|
|
|
func TestConfigServiceSettingsIsValid(t *testing.T) {
|
|
t.Run("local socket file should exist if local mode enabled", func(t *testing.T) {
|
|
cfg := Config{}
|
|
cfg.SetDefaults()
|
|
|
|
appErr := cfg.ServiceSettings.isValid()
|
|
require.Nil(t, appErr)
|
|
|
|
*cfg.ServiceSettings.EnableLocalMode = false
|
|
// we don't need to check as local mode is not enabled
|
|
*cfg.ServiceSettings.LocalModeSocketLocation = "an_invalid_path.socket"
|
|
appErr = cfg.ServiceSettings.isValid()
|
|
require.Nil(t, appErr)
|
|
|
|
// now we can check if the file exist or not
|
|
*cfg.ServiceSettings.EnableLocalMode = true
|
|
*cfg.ServiceSettings.LocalModeSocketLocation = "/invalid_directory/mattermost_local.socket"
|
|
appErr = cfg.ServiceSettings.isValid()
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, "model.config.is_valid.local_mode_socket.app_error", appErr.Id)
|
|
})
|
|
|
|
t.Run("CRT settings should have consistent values", func(t *testing.T) {
|
|
cfg := Config{}
|
|
cfg.SetDefaults()
|
|
|
|
appErr := cfg.ServiceSettings.isValid()
|
|
require.Nil(t, appErr)
|
|
|
|
*cfg.ServiceSettings.CollapsedThreads = CollapsedThreadsDisabled
|
|
appErr = cfg.ServiceSettings.isValid()
|
|
require.Nil(t, appErr)
|
|
|
|
*cfg.ServiceSettings.ThreadAutoFollow = false
|
|
appErr = cfg.ServiceSettings.isValid()
|
|
require.Nil(t, appErr)
|
|
|
|
*cfg.ServiceSettings.CollapsedThreads = CollapsedThreadsDefaultOff
|
|
appErr = cfg.ServiceSettings.isValid()
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, "model.config.is_valid.collapsed_threads.autofollow.app_error", appErr.Id)
|
|
|
|
*cfg.ServiceSettings.CollapsedThreads = CollapsedThreadsDefaultOn
|
|
appErr = cfg.ServiceSettings.isValid()
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, "model.config.is_valid.collapsed_threads.autofollow.app_error", appErr.Id)
|
|
|
|
*cfg.ServiceSettings.CollapsedThreads = CollapsedThreadsAlwaysOn
|
|
appErr = cfg.ServiceSettings.isValid()
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, "model.config.is_valid.collapsed_threads.autofollow.app_error", appErr.Id)
|
|
|
|
*cfg.ServiceSettings.ThreadAutoFollow = true
|
|
*cfg.ServiceSettings.CollapsedThreads = "test_status"
|
|
appErr = cfg.ServiceSettings.isValid()
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, "model.config.is_valid.collapsed_threads.app_error", appErr.Id)
|
|
})
|
|
}
|
|
|
|
func TestConfigDefaultCallsPluginState(t *testing.T) {
|
|
t.Run("should enable Calls plugin by default on self-hosted", func(t *testing.T) {
|
|
c1 := Config{}
|
|
c1.SetDefaults()
|
|
|
|
assert.True(t, c1.PluginSettings.PluginStates["com.mattermost.calls"].Enable)
|
|
})
|
|
|
|
t.Run("should enable Calls plugin by default on Cloud", func(t *testing.T) {
|
|
os.Setenv("MM_CLOUD_INSTALLATION_ID", "test")
|
|
defer os.Unsetenv("MM_CLOUD_INSTALLATION_ID")
|
|
c1 := Config{}
|
|
c1.SetDefaults()
|
|
|
|
assert.True(t, c1.PluginSettings.PluginStates["com.mattermost.calls"].Enable)
|
|
})
|
|
|
|
t.Run("should not re-enable Calls plugin after it has been disabled", func(t *testing.T) {
|
|
c1 := Config{
|
|
PluginSettings: PluginSettings{
|
|
PluginStates: map[string]*PluginState{
|
|
"com.mattermost.calls": {
|
|
Enable: false,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
c1.SetDefaults()
|
|
assert.False(t, c1.PluginSettings.PluginStates["com.mattermost.calls"].Enable)
|
|
})
|
|
}
|
|
|
|
func TestConfigDefaultAIPluginState(t *testing.T) {
|
|
t.Run("should enable AI plugin by default on self-hosted", func(t *testing.T) {
|
|
c1 := Config{}
|
|
c1.SetDefaults()
|
|
|
|
assert.True(t, c1.PluginSettings.PluginStates["mattermost-ai"].Enable)
|
|
})
|
|
|
|
t.Run("should enable AI plugin by default on Cloud", func(t *testing.T) {
|
|
os.Setenv("MM_CLOUD_INSTALLATION_ID", "test")
|
|
defer os.Unsetenv("MM_CLOUD_INSTALLATION_ID")
|
|
c1 := Config{}
|
|
c1.SetDefaults()
|
|
|
|
assert.True(t, c1.PluginSettings.PluginStates["mattermost-ai"].Enable)
|
|
})
|
|
|
|
t.Run("should not re-enable AI plugin after it has been disabled", func(t *testing.T) {
|
|
c1 := Config{
|
|
PluginSettings: PluginSettings{
|
|
PluginStates: map[string]*PluginState{
|
|
"mattermost-ai": {
|
|
Enable: false,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
c1.SetDefaults()
|
|
assert.False(t, c1.PluginSettings.PluginStates["mattermost-ai"].Enable)
|
|
})
|
|
}
|
|
|
|
func TestConfigGetMessageRetentionHours(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
config Config
|
|
value int
|
|
}{
|
|
{
|
|
name: "should return MessageRetentionDays config value in hours by default",
|
|
config: Config{},
|
|
value: 8760,
|
|
},
|
|
{
|
|
name: "should return MessageRetentionHours config value",
|
|
config: Config{
|
|
DataRetentionSettings: DataRetentionSettings{
|
|
MessageRetentionHours: NewPointer(48),
|
|
},
|
|
},
|
|
value: 48,
|
|
},
|
|
{
|
|
name: "should return MessageRetentionHours config value",
|
|
config: Config{
|
|
DataRetentionSettings: DataRetentionSettings{
|
|
MessageRetentionDays: NewPointer(50),
|
|
MessageRetentionHours: NewPointer(48),
|
|
},
|
|
},
|
|
value: 48,
|
|
},
|
|
{
|
|
name: "should return MessageRetentionDays config value in hours",
|
|
config: Config{
|
|
DataRetentionSettings: DataRetentionSettings{
|
|
MessageRetentionDays: NewPointer(50),
|
|
MessageRetentionHours: NewPointer(0),
|
|
},
|
|
},
|
|
value: 1200,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
test.config.SetDefaults()
|
|
|
|
require.Equal(t, test.value, test.config.DataRetentionSettings.GetMessageRetentionHours())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfigGetFileRetentionHours(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
config Config
|
|
value int
|
|
}{
|
|
{
|
|
name: "should return FileRetentionDays config value in hours by default",
|
|
config: Config{},
|
|
value: 8760,
|
|
},
|
|
{
|
|
name: "should return FileRetentionHours config value",
|
|
config: Config{
|
|
DataRetentionSettings: DataRetentionSettings{
|
|
FileRetentionHours: NewPointer(48),
|
|
},
|
|
},
|
|
value: 48,
|
|
},
|
|
{
|
|
name: "should return FileRetentionHours config value",
|
|
config: Config{
|
|
DataRetentionSettings: DataRetentionSettings{
|
|
FileRetentionDays: NewPointer(50),
|
|
FileRetentionHours: NewPointer(48),
|
|
},
|
|
},
|
|
value: 48,
|
|
},
|
|
{
|
|
name: "should return FileRetentionDays config value in hours",
|
|
config: Config{
|
|
DataRetentionSettings: DataRetentionSettings{
|
|
FileRetentionDays: NewPointer(50),
|
|
FileRetentionHours: NewPointer(0),
|
|
},
|
|
},
|
|
value: 1200,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
test.config.SetDefaults()
|
|
|
|
require.Equal(t, test.value, test.config.DataRetentionSettings.GetFileRetentionHours())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfigDefaultConnectedWorkspacesSettings(t *testing.T) {
|
|
t.Run("if the config is new, default values should be established", func(t *testing.T) {
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
require.False(t, *c.ConnectedWorkspacesSettings.EnableSharedChannels)
|
|
require.False(t, *c.ConnectedWorkspacesSettings.EnableRemoteClusterService)
|
|
})
|
|
|
|
t.Run("if the config is being updated and server federation settings had no values, experimental settings values should be migrated", func(t *testing.T) {
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
c.ConnectedWorkspacesSettings = ConnectedWorkspacesSettings{}
|
|
c.ExperimentalSettings.EnableSharedChannels = NewPointer(true)
|
|
c.ExperimentalSettings.EnableRemoteClusterService = NewPointer(false)
|
|
|
|
c.SetDefaults()
|
|
require.True(t, *c.ConnectedWorkspacesSettings.EnableSharedChannels)
|
|
require.False(t, *c.ConnectedWorkspacesSettings.EnableRemoteClusterService)
|
|
})
|
|
|
|
t.Run("if the config is being updated and server federation settings already have values, they should not change", func(t *testing.T) {
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
c.ConnectedWorkspacesSettings.EnableSharedChannels = NewPointer(false)
|
|
c.ConnectedWorkspacesSettings.EnableRemoteClusterService = NewPointer(true)
|
|
c.ExperimentalSettings.EnableSharedChannels = NewPointer(true)
|
|
c.ExperimentalSettings.EnableRemoteClusterService = NewPointer(false)
|
|
|
|
c.SetDefaults()
|
|
require.False(t, *c.ConnectedWorkspacesSettings.EnableSharedChannels)
|
|
require.True(t, *c.ConnectedWorkspacesSettings.EnableRemoteClusterService)
|
|
})
|
|
}
|
|
|
|
func TestExperimentalAuditSettingsIsValid(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for name, test := range map[string]struct {
|
|
ExperimentalAuditSettings ExperimentalAuditSettings
|
|
ExpectError bool
|
|
}{
|
|
"empty settings": {
|
|
ExperimentalAuditSettings: ExperimentalAuditSettings{},
|
|
ExpectError: false,
|
|
},
|
|
"file enabled with empty filename": {
|
|
ExperimentalAuditSettings: ExperimentalAuditSettings{
|
|
FileEnabled: NewPointer(true),
|
|
FileName: NewPointer(""),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
"file enabled with valid filename": {
|
|
ExperimentalAuditSettings: ExperimentalAuditSettings{
|
|
FileEnabled: NewPointer(true),
|
|
FileName: NewPointer("audit.log"),
|
|
FileMaxSizeMB: NewPointer(100),
|
|
FileMaxAgeDays: NewPointer(5),
|
|
FileMaxBackups: NewPointer(10),
|
|
FileMaxQueueSize: NewPointer(1000),
|
|
},
|
|
ExpectError: false,
|
|
},
|
|
"invalid file max size": {
|
|
ExperimentalAuditSettings: ExperimentalAuditSettings{
|
|
FileEnabled: NewPointer(true),
|
|
FileName: NewPointer("audit.log"),
|
|
FileMaxSizeMB: NewPointer(0),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
"negative file max size": {
|
|
ExperimentalAuditSettings: ExperimentalAuditSettings{
|
|
FileEnabled: NewPointer(true),
|
|
FileName: NewPointer("audit.log"),
|
|
FileMaxSizeMB: NewPointer(-10),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
"negative file max age": {
|
|
ExperimentalAuditSettings: ExperimentalAuditSettings{
|
|
FileEnabled: NewPointer(true),
|
|
FileName: NewPointer("audit.log"),
|
|
FileMaxSizeMB: NewPointer(100),
|
|
FileMaxAgeDays: NewPointer(-5),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
"negative file max backups": {
|
|
ExperimentalAuditSettings: ExperimentalAuditSettings{
|
|
FileEnabled: NewPointer(true),
|
|
FileName: NewPointer("audit.log"),
|
|
FileMaxSizeMB: NewPointer(100),
|
|
FileMaxAgeDays: NewPointer(5),
|
|
FileMaxBackups: NewPointer(-10),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
"zero file max queue size": {
|
|
ExperimentalAuditSettings: ExperimentalAuditSettings{
|
|
FileEnabled: NewPointer(true),
|
|
FileName: NewPointer("audit.log"),
|
|
FileMaxSizeMB: NewPointer(100),
|
|
FileMaxAgeDays: NewPointer(5),
|
|
FileMaxBackups: NewPointer(10),
|
|
FileMaxQueueSize: NewPointer(0),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
"negative file max queue size": {
|
|
ExperimentalAuditSettings: ExperimentalAuditSettings{
|
|
FileEnabled: NewPointer(true),
|
|
FileName: NewPointer("audit.log"),
|
|
FileMaxSizeMB: NewPointer(100),
|
|
FileMaxAgeDays: NewPointer(5),
|
|
FileMaxBackups: NewPointer(10),
|
|
FileMaxQueueSize: NewPointer(-1000),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
"AdvancedLoggingJSON has JSON error ": {
|
|
ExperimentalAuditSettings: ExperimentalAuditSettings{
|
|
AdvancedLoggingJSON: json.RawMessage(`
|
|
{
|
|
"foo": "bar",
|
|
`),
|
|
},
|
|
ExpectError: true,
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
test.ExperimentalAuditSettings.SetDefaults()
|
|
|
|
appErr := test.ExperimentalAuditSettings.isValid()
|
|
if test.ExpectError {
|
|
require.NotNil(t, appErr)
|
|
} else {
|
|
require.Nil(t, appErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFilterConfig(t *testing.T) {
|
|
t.Run("should clear default values", func(t *testing.T) {
|
|
cfg := &Config{}
|
|
cfg.SetDefaults()
|
|
|
|
m, err := FilterConfig(cfg, ConfigFilterOptions{
|
|
GetConfigOptions: GetConfigOptions{
|
|
RemoveDefaults: true,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.Empty(t, m)
|
|
|
|
cfg.ServiceSettings = ServiceSettings{
|
|
EnableLocalMode: NewPointer(true),
|
|
}
|
|
|
|
m, err = FilterConfig(cfg, ConfigFilterOptions{
|
|
GetConfigOptions: GetConfigOptions{
|
|
RemoveDefaults: true,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, m)
|
|
require.Equal(t, true, m["ServiceSettings"].(map[string]any)["EnableLocalMode"])
|
|
})
|
|
|
|
t.Run("should clear masked config values", func(t *testing.T) {
|
|
cfg := &Config{}
|
|
cfg.SetDefaults()
|
|
|
|
dsn := "somedb://user:password@localhost:5432/mattermost"
|
|
cfg.SqlSettings.DataSource = NewPointer(dsn)
|
|
|
|
m, err := FilterConfig(cfg, ConfigFilterOptions{
|
|
GetConfigOptions: GetConfigOptions{
|
|
RemoveDefaults: true,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, m)
|
|
require.Equal(t, dsn, m["SqlSettings"].(map[string]any)["DataSource"])
|
|
|
|
cfg.Sanitize(nil, nil)
|
|
m, err = FilterConfig(cfg, ConfigFilterOptions{
|
|
GetConfigOptions: GetConfigOptions{
|
|
RemoveDefaults: true,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, m)
|
|
require.Equal(t, FakeSetting, m["SqlSettings"].(map[string]any)["DataSource"])
|
|
|
|
cfg.Sanitize(nil, nil)
|
|
m, err = FilterConfig(cfg, ConfigFilterOptions{
|
|
GetConfigOptions: GetConfigOptions{
|
|
RemoveDefaults: true,
|
|
RemoveMasked: true,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.Empty(t, m)
|
|
|
|
cfg.SqlSettings.DriverName = NewPointer("mysql")
|
|
m, err = FilterConfig(cfg, ConfigFilterOptions{
|
|
GetConfigOptions: GetConfigOptions{
|
|
RemoveDefaults: true,
|
|
RemoveMasked: true,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, m)
|
|
require.Equal(t, "mysql", m["SqlSettings"].(map[string]any)["DriverName"])
|
|
})
|
|
|
|
t.Run("should not clear non primitive types", func(t *testing.T) {
|
|
cfg := &Config{}
|
|
cfg.SetDefaults()
|
|
|
|
cfg.TeamSettings.ExperimentalDefaultChannels = []string{"ch-a", "ch-b"}
|
|
m, err := FilterConfig(cfg, ConfigFilterOptions{
|
|
GetConfigOptions: GetConfigOptions{
|
|
RemoveDefaults: true,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, m)
|
|
require.ElementsMatch(t, []string{"ch-a", "ch-b"}, m["TeamSettings"].(map[string]any)["ExperimentalDefaultChannels"])
|
|
})
|
|
|
|
t.Run("should be able to handle nil values", func(t *testing.T) {
|
|
var cfg *Config
|
|
|
|
m, err := FilterConfig(cfg, ConfigFilterOptions{
|
|
GetConfigOptions: GetConfigOptions{
|
|
RemoveDefaults: true,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.Empty(t, m)
|
|
})
|
|
|
|
t.Run("should be able to handle float64 values", func(t *testing.T) {
|
|
cfg := &Config{}
|
|
cfg.SetDefaults()
|
|
cfg.PluginSettings.Plugins = map[string]map[string]any{
|
|
"com.mattermost.plugin-a": {
|
|
"setting": 1.0,
|
|
},
|
|
}
|
|
|
|
m, err := FilterConfig(cfg, ConfigFilterOptions{
|
|
GetConfigOptions: GetConfigOptions{
|
|
RemoveDefaults: true,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1.0, m["PluginSettings"].(map[string]any)["Plugins"].(map[string]any)["com.mattermost.plugin-a"].(map[string]any)["setting"])
|
|
})
|
|
|
|
t.Run("should be able to filter specific tag", func(t *testing.T) {
|
|
cfg := &Config{}
|
|
cfg.SetDefaults()
|
|
|
|
m, err := FilterConfig(cfg, ConfigFilterOptions{
|
|
GetConfigOptions: GetConfigOptions{},
|
|
TagFilters: []FilterTag{
|
|
{
|
|
TagType: ConfigAccessTagType,
|
|
TagName: ConfigAccessTagCloudRestrictable,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, m)
|
|
|
|
fileSettings, ok := m["FileSettings"]
|
|
require.True(t, ok)
|
|
|
|
enableFileAttachments, ok := fileSettings.(map[string]any)["EnableFileAttachments"]
|
|
require.True(t, ok)
|
|
require.Equal(t, true, enableFileAttachments)
|
|
|
|
// All fields of SqlSettings are ConfigAccessTagCloudRestrictable
|
|
_, ok = m["SqlSettings"]
|
|
require.False(t, ok)
|
|
})
|
|
|
|
t.Run("should be able to filter multiple tags", func(t *testing.T) {
|
|
cfg := &Config{}
|
|
cfg.SetDefaults()
|
|
|
|
m, err := FilterConfig(cfg, ConfigFilterOptions{
|
|
GetConfigOptions: GetConfigOptions{},
|
|
TagFilters: []FilterTag{
|
|
{
|
|
TagType: ConfigAccessTagType,
|
|
TagName: "site_file_sharing_and_downloads",
|
|
},
|
|
{
|
|
TagType: ConfigAccessTagType,
|
|
TagName: ConfigAccessTagCloudRestrictable,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, m)
|
|
|
|
fileSettings, ok := m["FileSettings"]
|
|
require.True(t, ok)
|
|
// EnableFileAttachments has "site_file_sharing_and_downloads" tag
|
|
_, ok = fileSettings.(map[string]any)["EnableFileAttachments"]
|
|
require.False(t, ok)
|
|
|
|
// All fields of SqlSettings are ConfigAccessTagCloudRestrictable
|
|
_, ok = m["SqlSettings"]
|
|
require.False(t, ok)
|
|
})
|
|
}
|
|
|
|
func TestAutoTranslationSettingsDefaults(t *testing.T) {
|
|
t.Run("should set default values", func(t *testing.T) {
|
|
c := Config{}
|
|
c.SetDefaults()
|
|
|
|
require.False(t, *c.AutoTranslationSettings.Enable)
|
|
require.Equal(t, "", *c.AutoTranslationSettings.Provider)
|
|
require.Equal(t, 800, *c.AutoTranslationSettings.TimeoutsMs.NewPost)
|
|
require.Equal(t, 2000, *c.AutoTranslationSettings.TimeoutsMs.Fetch)
|
|
require.Equal(t, 300, *c.AutoTranslationSettings.TimeoutsMs.Notification)
|
|
require.Equal(t, "", *c.AutoTranslationSettings.LibreTranslate.URL)
|
|
require.Equal(t, "", *c.AutoTranslationSettings.LibreTranslate.APIKey)
|
|
// TODO: Enable Agents provider in future release
|
|
// require.Equal(t, "", *c.AutoTranslationSettings.Agents.BotUserId)
|
|
})
|
|
}
|
|
|
|
func TestAutoTranslationSettingsIsValid(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
settings AutoTranslationSettings
|
|
expectError bool
|
|
errorId string
|
|
}{
|
|
{
|
|
name: "disabled settings should be valid",
|
|
settings: AutoTranslationSettings{
|
|
Enable: NewPointer(false),
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "enabled with no provider should fail",
|
|
settings: AutoTranslationSettings{
|
|
Enable: NewPointer(true),
|
|
Provider: nil,
|
|
},
|
|
expectError: true,
|
|
errorId: "model.config.is_valid.autotranslation.provider.app_error",
|
|
},
|
|
{
|
|
name: "enabled with unsupported provider should fail",
|
|
settings: AutoTranslationSettings{
|
|
Enable: NewPointer(true),
|
|
Provider: NewPointer("unsupported"),
|
|
},
|
|
expectError: true,
|
|
errorId: "model.config.is_valid.autotranslation.provider.unsupported.app_error",
|
|
},
|
|
{
|
|
name: "libretranslate without URL should fail",
|
|
settings: AutoTranslationSettings{
|
|
Enable: NewPointer(true),
|
|
Provider: NewPointer("libretranslate"),
|
|
LibreTranslate: &LibreTranslateProviderSettings{
|
|
URL: NewPointer(""),
|
|
},
|
|
},
|
|
expectError: true,
|
|
errorId: "model.config.is_valid.autotranslation.libretranslate.url.app_error",
|
|
},
|
|
// TODO: Enable Agents provider in future release
|
|
// {
|
|
// name: "agents without bot user ID should fail",
|
|
// settings: AutoTranslationSettings{
|
|
// Enable: NewPointer(true),
|
|
// Provider: NewPointer("agents"),
|
|
// Agents: &AgentsProviderSettings{
|
|
// BotUserId: NewPointer(""),
|
|
// },
|
|
// },
|
|
// expectError: true,
|
|
// errorId: "model.config.is_valid.autotranslation.agents.bot_user_id.app_error",
|
|
// },
|
|
{
|
|
name: "valid libretranslate settings",
|
|
settings: AutoTranslationSettings{
|
|
Enable: NewPointer(true),
|
|
Provider: NewPointer("libretranslate"),
|
|
LibreTranslate: &LibreTranslateProviderSettings{
|
|
URL: NewPointer("https://lt.example.com"),
|
|
APIKey: NewPointer("optional-key"),
|
|
},
|
|
},
|
|
expectError: false,
|
|
},
|
|
// TODO: Enable Agents provider in future release
|
|
// {
|
|
// name: "valid agents settings",
|
|
// settings: AutoTranslationSettings{
|
|
// Enable: NewPointer(true),
|
|
// Provider: NewPointer("agents"),
|
|
// Agents: &AgentsProviderSettings{
|
|
// BotUserId: NewPointer("bot123"),
|
|
// },
|
|
// },
|
|
// expectError: false,
|
|
// },
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
tc.settings.SetDefaults()
|
|
err := tc.settings.isValid()
|
|
if tc.expectError {
|
|
require.NotNil(t, err)
|
|
require.Equal(t, tc.errorId, err.Id)
|
|
} else {
|
|
require.Nil(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|