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>
1028 lines
30 KiB
Go
1028 lines
30 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package commands
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/spf13/cobra"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/printer"
|
|
)
|
|
|
|
const (
|
|
configFilePayload = "{\"TeamSettings\": {\"SiteName\": \"ADifferentName\"}}"
|
|
configFilePluginPayload = "{\"PluginSettings\": {\"Plugins\": {\"plugin.1\": {\"new\": \"key\", \"existing\": \"replacement\"}, \"plugin.2\": {\"this is\": \"new\"}}}}"
|
|
)
|
|
|
|
func (s *MmctlUnitTestSuite) TestConfigGetCmd() {
|
|
s.Run("Get a string config value for a given key", func() {
|
|
printer.Clean()
|
|
args := []string{"SqlSettings.DriverName"}
|
|
outputConfig := &model.Config{}
|
|
outputConfig.SetDefaults()
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(outputConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configGetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal("postgres", *(printer.GetLines()[0].(*string)))
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Get an int config value for a given key", func() {
|
|
printer.Clean()
|
|
args := []string{"SqlSettings.MaxIdleConns"}
|
|
outputConfig := &model.Config{}
|
|
outputConfig.SetDefaults()
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(outputConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configGetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(*(printer.GetLines()[0].(*int)), 50)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Get an int64 config value for a given key", func() {
|
|
printer.Clean()
|
|
args := []string{"FileSettings.MaxFileSize"}
|
|
outputConfig := &model.Config{}
|
|
outputConfig.SetDefaults()
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(outputConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configGetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(int64(100*(1<<20)), *(printer.GetLines()[0].(*int64)))
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Get a boolean config value for a given key", func() {
|
|
printer.Clean()
|
|
args := []string{"SqlSettings.Trace"}
|
|
outputConfig := &model.Config{}
|
|
outputConfig.SetDefaults()
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(outputConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configGetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(*(printer.GetLines()[0].(*bool)), false)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Get a slice of string config value for a given key", func() {
|
|
printer.Clean()
|
|
args := []string{"SqlSettings.DataSourceReplicas"}
|
|
outputConfig := &model.Config{}
|
|
outputConfig.SetDefaults()
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(outputConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configGetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], []string{})
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Get config struct for a given key", func() {
|
|
printer.Clean()
|
|
args := []string{"SqlSettings"}
|
|
outputConfig := &model.Config{}
|
|
outputConfig.SetDefaults()
|
|
sqlSettings := model.SqlSettings{}
|
|
sqlSettings.SetDefaults(false)
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(outputConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configGetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], sqlSettings)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Get error if the key doesn't exist", func() {
|
|
printer.Clean()
|
|
args := []string{"SqlSettings.WrongKey"}
|
|
outputConfig := &model.Config{}
|
|
outputConfig.SetDefaults()
|
|
sqlSettings := model.SqlSettings{}
|
|
sqlSettings.SetDefaults(false)
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(outputConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configGetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().NotNil(err)
|
|
s.Require().Len(printer.GetLines(), 0)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Should handle the response error", func() {
|
|
printer.Clean()
|
|
args := []string{"SqlSettings.DriverName"}
|
|
outputConfig := &model.Config{}
|
|
outputConfig.SetDefaults()
|
|
sqlSettings := model.SqlSettings{}
|
|
sqlSettings.SetDefaults(false)
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(outputConfig, &model.Response{StatusCode: 500}, errors.New("")).
|
|
Times(1)
|
|
|
|
err := configGetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().NotNil(err)
|
|
s.Require().Len(printer.GetLines(), 0)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Get value if the key points to a map element", func() {
|
|
outputConfig := &model.Config{}
|
|
pluginState := &model.PluginState{Enable: true}
|
|
pluginSettings := map[string]any{
|
|
"test1": 1,
|
|
"test2": []string{"a", "b"},
|
|
"test3": map[string]string{"a": "b"},
|
|
}
|
|
outputConfig.PluginSettings.PluginStates = map[string]*model.PluginState{
|
|
"com.mattermost.testplugin": pluginState,
|
|
}
|
|
outputConfig.PluginSettings.Plugins = map[string]map[string]any{
|
|
"com.mattermost.testplugin": pluginSettings,
|
|
}
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(outputConfig, &model.Response{}, nil).
|
|
Times(7)
|
|
|
|
printer.Clean()
|
|
err := configGetCmdF(s.client, &cobra.Command{}, []string{"PluginSettings.PluginStates.com.mattermost.testplugin"})
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], pluginState)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
|
|
printer.Clean()
|
|
err = configGetCmdF(s.client, &cobra.Command{}, []string{"PluginSettings.Plugins.com.mattermost.testplugin"})
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], pluginSettings)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
|
|
printer.Clean()
|
|
err = configGetCmdF(s.client, &cobra.Command{}, []string{"PluginSettings.Plugins.com.mattermost.testplugin.test1"})
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], 1)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
|
|
printer.Clean()
|
|
err = configGetCmdF(s.client, &cobra.Command{}, []string{"PluginSettings.Plugins.com.mattermost.testplugin.test2"})
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], []string{"a", "b"})
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
|
|
printer.Clean()
|
|
err = configGetCmdF(s.client, &cobra.Command{}, []string{"PluginSettings.Plugins.com.mattermost.testplugin.test3"})
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], map[string]string{"a": "b"})
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
|
|
printer.Clean()
|
|
err = configGetCmdF(s.client, &cobra.Command{}, []string{"PluginSettings.Plugins.com.mattermost.testplugin.test3.a"})
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], "b")
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Get error value if the key points to a missing map element", func() {
|
|
printer.Clean()
|
|
args := []string{"PluginSettings.PluginStates.com.mattermost.testplugin.x"}
|
|
outputConfig := &model.Config{}
|
|
pluginState := &model.PluginState{Enable: true}
|
|
outputConfig.PluginSettings.PluginStates = map[string]*model.PluginState{
|
|
"com.mattermost.testplugin": pluginState,
|
|
}
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(outputConfig, &model.Response{}, nil).
|
|
Times(0)
|
|
|
|
err := configGetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().NotNil(err)
|
|
s.Require().Len(printer.GetLines(), 0)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Get cloud restricted error value if the path is cloud restricted and value is nil", func() {
|
|
printer.Clean()
|
|
args := []string{"ServiceSettings.EnableDeveloper"}
|
|
outputConfig := &model.Config{}
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(outputConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configGetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().NotNil(err)
|
|
s.Require().Len(printer.GetLines(), 0)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
}
|
|
|
|
func (s *MmctlUnitTestSuite) TestConfigSetCmd() {
|
|
s.Run("Set a string config value for a given key", func() {
|
|
printer.Clean()
|
|
args := []string{"SqlSettings.DriverName", "postgres"}
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
inputConfig := &model.Config{}
|
|
inputConfig.SetDefaults()
|
|
changedValue := "postgres"
|
|
inputConfig.SqlSettings.DriverName = &changedValue
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
s.client.
|
|
EXPECT().
|
|
PatchConfig(context.TODO(), inputConfig).
|
|
Return(inputConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configSetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], inputConfig)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Set an int config value for a given key", func() {
|
|
printer.Clean()
|
|
args := []string{"SqlSettings.MaxIdleConns", "20"}
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
inputConfig := &model.Config{}
|
|
inputConfig.SetDefaults()
|
|
changedValue := 20
|
|
inputConfig.SqlSettings.MaxIdleConns = &changedValue
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
s.client.
|
|
EXPECT().
|
|
PatchConfig(context.TODO(), inputConfig).
|
|
Return(inputConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configSetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], inputConfig)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Set an int64 config value for a given key", func() {
|
|
printer.Clean()
|
|
args := []string{"FileSettings.MaxFileSize", "52428800"}
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
inputConfig := &model.Config{}
|
|
inputConfig.SetDefaults()
|
|
changedValue := int64(52428800)
|
|
inputConfig.FileSettings.MaxFileSize = &changedValue
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
s.client.
|
|
EXPECT().
|
|
PatchConfig(context.TODO(), inputConfig).
|
|
Return(inputConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configSetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], inputConfig)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Set a boolean config value for a given key", func() {
|
|
printer.Clean()
|
|
args := []string{"SqlSettings.Trace", "true"}
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
inputConfig := &model.Config{}
|
|
inputConfig.SetDefaults()
|
|
changedValue := true
|
|
inputConfig.SqlSettings.Trace = &changedValue
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
s.client.
|
|
EXPECT().
|
|
PatchConfig(context.TODO(), inputConfig).
|
|
Return(inputConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configSetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], inputConfig)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Set a slice of string config value for a given key", func() {
|
|
printer.Clean()
|
|
args := []string{"SqlSettings.DataSourceReplicas", "test1", "test2"}
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
inputConfig := &model.Config{}
|
|
inputConfig.SetDefaults()
|
|
inputConfig.SqlSettings.DataSourceReplicas = []string{"test1", "test2"}
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
s.client.
|
|
EXPECT().
|
|
PatchConfig(context.TODO(), inputConfig).
|
|
Return(inputConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configSetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], inputConfig)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Should get an error if a string is passed while trying to set a slice", func() {
|
|
printer.Clean()
|
|
args := []string{"SqlSettings.DataSourceReplicas", "[\"test1\", \"test2\"]"}
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
inputConfig := &model.Config{}
|
|
inputConfig.SetDefaults()
|
|
inputConfig.SqlSettings.DataSourceReplicas = []string{"test1", "test2"}
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configSetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().NotNil(err)
|
|
s.Require().Len(printer.GetLines(), 0)
|
|
})
|
|
|
|
s.Run("Get error if the key doesn't exist", func() {
|
|
printer.Clean()
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
args := []string{"SqlSettings.WrongKey", "test1"}
|
|
inputConfig := &model.Config{}
|
|
inputConfig.SetDefaults()
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configSetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().NotNil(err)
|
|
s.Require().Len(printer.GetLines(), 0)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Should handle response error from the server", func() {
|
|
printer.Clean()
|
|
args := []string{"SqlSettings.DriverName", "postgres"}
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
inputConfig := &model.Config{}
|
|
inputConfig.SetDefaults()
|
|
changedValue := "postgres"
|
|
inputConfig.SqlSettings.DriverName = &changedValue
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
s.client.
|
|
EXPECT().
|
|
PatchConfig(context.TODO(), inputConfig).
|
|
Return(inputConfig, &model.Response{StatusCode: 500}, errors.New("")).
|
|
Times(1)
|
|
|
|
err := configSetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().NotNil(err)
|
|
s.Require().Len(printer.GetLines(), 0)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Set a field inside a map", func() {
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
defaultConfig.PluginSettings.PluginStates = map[string]*model.PluginState{
|
|
"com.mattermost.testplugin": {Enable: false},
|
|
}
|
|
pluginSettings := map[string]any{
|
|
"test1": 1,
|
|
"test2": []string{"a", "b"},
|
|
"test3": map[string]any{"a": "b"},
|
|
}
|
|
defaultConfig.PluginSettings.Plugins = map[string]map[string]any{
|
|
"com.mattermost.testplugin": pluginSettings,
|
|
}
|
|
|
|
inputConfig := &model.Config{}
|
|
inputConfig.SetDefaults()
|
|
inputConfig.PluginSettings.PluginStates = map[string]*model.PluginState{
|
|
"com.mattermost.testplugin": {Enable: true},
|
|
}
|
|
inputConfig.PluginSettings.Plugins = map[string]map[string]any{
|
|
"com.mattermost.testplugin": pluginSettings,
|
|
}
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(3)
|
|
|
|
s.client.
|
|
EXPECT().
|
|
PatchConfig(context.TODO(), inputConfig).
|
|
Return(inputConfig, &model.Response{}, nil).
|
|
Times(3)
|
|
|
|
printer.Clean()
|
|
err := configSetCmdF(s.client, &cobra.Command{}, []string{"PluginSettings.PluginStates.com.mattermost.testplugin.Enable", "true"})
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
|
|
printer.Clean()
|
|
err = configSetCmdF(s.client, &cobra.Command{}, []string{"PluginSettings.Plugins.com.mattermost.testplugin.test1", "123"})
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
|
|
printer.Clean()
|
|
err = configSetCmdF(s.client, &cobra.Command{}, []string{"PluginSettings.Plugins.com.mattermost.testplugin.test3.a", "123"})
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Try to set a field inside a map for incorrect field, get error", func() {
|
|
printer.Clean()
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
defaultConfig.PluginSettings.PluginStates = map[string]*model.PluginState{
|
|
"com.mattermost.testplugin": {Enable: true},
|
|
}
|
|
args := []string{"PluginSettings.PluginStates.com.mattermost.testplugin.x", "true"}
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configSetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().NotNil(err)
|
|
s.Require().Len(printer.GetLines(), 0)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Set a config value for a cloud restricted config path", func() {
|
|
printer.Clean()
|
|
args := []string{"ServiceSettings.EnableDeveloper", "true"}
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
js, err := defaultConfig.ToJSONFiltered(model.ConfigAccessTagType, model.ConfigAccessTagCloudRestrictable)
|
|
s.Require().NoError(err)
|
|
defaultConfig = model.ConfigFromJSON(bytes.NewBuffer(js))
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err = configSetCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().EqualError(err, fmt.Sprintf("changing this config path: %s is restricted in a cloud environment", "ServiceSettings.EnableDeveloper"))
|
|
s.Require().Len(printer.GetLines(), 0)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
}
|
|
|
|
func (s *MmctlUnitTestSuite) TestConfigPatchCmd() {
|
|
tmpFile, err := os.CreateTemp(os.TempDir(), "config_*.json")
|
|
s.Require().NoError(err)
|
|
|
|
invalidFile, err := os.CreateTemp(os.TempDir(), "invalid_config_*.json")
|
|
s.Require().NoError(err)
|
|
|
|
pluginFile, err := os.CreateTemp(os.TempDir(), "plugin_config_*.json")
|
|
s.Require().NoError(err)
|
|
|
|
_, err = tmpFile.Write([]byte(configFilePayload))
|
|
s.Require().NoError(err)
|
|
|
|
_, err = pluginFile.Write([]byte(configFilePluginPayload))
|
|
s.Require().NoError(err)
|
|
|
|
defer func() {
|
|
os.Remove(tmpFile.Name())
|
|
os.Remove(invalidFile.Name())
|
|
os.Remove(pluginFile.Name())
|
|
}()
|
|
|
|
s.Run("Patch config with a valid file", func() {
|
|
printer.Clean()
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
brandValue := "BrandText"
|
|
defaultConfig.TeamSettings.CustomBrandText = &brandValue
|
|
|
|
inputConfig := &model.Config{}
|
|
inputConfig.SetDefaults()
|
|
changedValue := "ADifferentName"
|
|
inputConfig.TeamSettings.SiteName = &changedValue
|
|
inputConfig.TeamSettings.CustomBrandText = &brandValue
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
s.client.
|
|
EXPECT().
|
|
PatchConfig(context.TODO(), inputConfig).
|
|
Return(inputConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err = configPatchCmdF(s.client, &cobra.Command{}, []string{tmpFile.Name()})
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], inputConfig)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Correctly patch with a valid file that affects plugins", func() {
|
|
printer.Clean()
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
defaultConfig.PluginSettings.Plugins = map[string]map[string]any{
|
|
"plugin.1": {
|
|
"existing": "value",
|
|
},
|
|
}
|
|
expectedPluginConfig := map[string]map[string]any{
|
|
"plugin.1": {
|
|
"new": "key",
|
|
"existing": "replacement",
|
|
},
|
|
"plugin.2": {
|
|
"this is": "new",
|
|
},
|
|
}
|
|
expectedConfig := &model.Config{}
|
|
expectedConfig.SetDefaults()
|
|
expectedConfig.PluginSettings.Plugins = expectedPluginConfig
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
s.client.
|
|
EXPECT().
|
|
PatchConfig(context.TODO(), expectedConfig).
|
|
Return(expectedConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err = configPatchCmdF(s.client, &cobra.Command{}, []string{pluginFile.Name()})
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], expectedConfig)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Fail to patch config if file is invalid", func() {
|
|
printer.Clean()
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err = configPatchCmdF(s.client, &cobra.Command{}, []string{invalidFile.Name()})
|
|
s.Require().NotNil(err)
|
|
})
|
|
|
|
s.Run("Fail to patch config if file not found", func() {
|
|
printer.Clean()
|
|
path := "/path/to/nonexistentfile"
|
|
errMsg := "open " + path + ": no such file or directory"
|
|
|
|
err = configPatchCmdF(s.client, &cobra.Command{}, []string{path})
|
|
s.Require().NotNil(err)
|
|
s.Require().EqualError(err, errMsg)
|
|
})
|
|
}
|
|
|
|
func (s *MmctlUnitTestSuite) TestConfigResetCmd() {
|
|
s.Run("Reset a single key", func() {
|
|
printer.Clean()
|
|
args := []string{"SqlSettings.DriverName"}
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
s.client.
|
|
EXPECT().
|
|
UpdateConfig(context.TODO(), defaultConfig).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
resetCmd := &cobra.Command{}
|
|
resetCmd.Flags().Bool("confirm", true, "")
|
|
err := configResetCmdF(s.client, resetCmd, args)
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], defaultConfig)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Reset a whole config section", func() {
|
|
printer.Clean()
|
|
args := []string{"SqlSettings"}
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
s.client.
|
|
EXPECT().
|
|
UpdateConfig(context.TODO(), defaultConfig).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
resetCmd := &cobra.Command{}
|
|
resetCmd.Flags().Bool("confirm", true, "")
|
|
_ = resetCmd.ParseFlags([]string{"confirm"})
|
|
err := configResetCmdF(s.client, resetCmd, args)
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Equal(printer.GetLines()[0], defaultConfig)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Should fail if the key doesn't exist", func() {
|
|
printer.Clean()
|
|
args := []string{"WrongKey"}
|
|
defaultConfig := &model.Config{}
|
|
defaultConfig.SetDefaults()
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(defaultConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
resetCmd := &cobra.Command{}
|
|
resetCmd.Flags().Bool("confirm", true, "")
|
|
_ = resetCmd.ParseFlags([]string{"confirm"})
|
|
err := configResetCmdF(s.client, resetCmd, args)
|
|
s.Require().NotNil(err)
|
|
s.Require().Len(printer.GetLines(), 0)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
}
|
|
|
|
func (s *MmctlUnitTestSuite) TestConfigShowCmd() {
|
|
s.Run("Should show config", func() {
|
|
printer.Clean()
|
|
mockConfig := &model.Config{}
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(mockConfig, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configShowCmdF(s.client, &cobra.Command{}, []string{})
|
|
s.Require().Nil(err)
|
|
s.Len(printer.GetLines(), 1)
|
|
s.Equal(mockConfig, printer.GetLines()[0])
|
|
s.Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Should return an error", func() {
|
|
printer.Clean()
|
|
configError := errors.New("config error")
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfig(context.TODO()).
|
|
Return(nil, &model.Response{}, configError).
|
|
Times(1)
|
|
|
|
err := configShowCmdF(s.client, &cobra.Command{}, []string{})
|
|
s.Require().NotNil(err)
|
|
s.EqualError(err, configError.Error())
|
|
})
|
|
}
|
|
|
|
func (s *MmctlUnitTestSuite) TestConfigReloadCmd() {
|
|
s.Run("Should reload config", func() {
|
|
printer.Clean()
|
|
|
|
s.client.
|
|
EXPECT().
|
|
ReloadConfig(context.TODO()).
|
|
Return(&model.Response{StatusCode: http.StatusOK}, nil).
|
|
Times(1)
|
|
|
|
err := configReloadCmdF(s.client, &cobra.Command{}, []string{})
|
|
s.Require().Nil(err)
|
|
s.Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Should fail on error when reload config", func() {
|
|
printer.Clean()
|
|
|
|
s.client.
|
|
EXPECT().
|
|
ReloadConfig(context.TODO()).
|
|
Return(&model.Response{StatusCode: http.StatusBadRequest}, errors.New("some-error")).
|
|
Times(1)
|
|
|
|
err := configReloadCmdF(s.client, &cobra.Command{}, []string{})
|
|
s.Require().NotNil(err)
|
|
})
|
|
}
|
|
|
|
func (s *MmctlUnitTestSuite) TestConfigMigrateCmd() {
|
|
s.Run("Should fail without the --local flag", func() {
|
|
printer.Clean()
|
|
args := []string{"from", "to"}
|
|
|
|
err := configMigrateCmdF(s.client, &cobra.Command{}, args)
|
|
s.Require().Error(err)
|
|
})
|
|
|
|
s.Run("Should be able to migrate config", func() {
|
|
printer.Clean()
|
|
args := []string{"from", "to"}
|
|
|
|
s.client.
|
|
EXPECT().
|
|
MigrateConfig(context.TODO(), args[0], args[1]).
|
|
Return(&model.Response{StatusCode: http.StatusOK}, nil).
|
|
Times(1)
|
|
|
|
cmd := &cobra.Command{}
|
|
cmd.Flags().Bool("local", true, "")
|
|
|
|
err := configMigrateCmdF(s.client, cmd, args)
|
|
s.Require().Nil(err)
|
|
s.Len(printer.GetErrorLines(), 0)
|
|
})
|
|
|
|
s.Run("Should fail on error when migrating config", func() {
|
|
printer.Clean()
|
|
args := []string{"from", "to"}
|
|
|
|
s.client.
|
|
EXPECT().
|
|
MigrateConfig(context.TODO(), args[0], args[1]).
|
|
Return(&model.Response{StatusCode: http.StatusBadRequest}, errors.New("some-error")).
|
|
Times(1)
|
|
|
|
cmd := &cobra.Command{}
|
|
cmd.Flags().Bool("local", true, "")
|
|
|
|
err := configMigrateCmdF(s.client, cmd, args)
|
|
s.Require().NotNil(err)
|
|
})
|
|
}
|
|
|
|
func TestCloudRestricted(t *testing.T) {
|
|
cfg := &model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
GoogleDeveloperKey: model.NewPointer("test"),
|
|
SiteURL: model.NewPointer("test"),
|
|
},
|
|
}
|
|
|
|
t.Run("Should return true if the config is cloud restricted", func(t *testing.T) {
|
|
path := "ServiceSettings.GoogleDeveloperKey"
|
|
|
|
require.True(t, cloudRestricted(cfg, parseConfigPath(path)))
|
|
})
|
|
|
|
t.Run("Should return false if the config is not cloud restricted", func(t *testing.T) {
|
|
path := "ServiceSettings.SiteURL"
|
|
|
|
require.False(t, cloudRestricted(cfg, parseConfigPath(path)))
|
|
})
|
|
|
|
t.Run("Should return false if the config is not cloud restricted and the path is not found", func(t *testing.T) {
|
|
path := "ServiceSettings.Unknown"
|
|
|
|
require.False(t, cloudRestricted(cfg, parseConfigPath(path)))
|
|
})
|
|
|
|
t.Run("Should return true if the config is cloud restricted and the value is not found", func(t *testing.T) {
|
|
path := "ServiceSettings.EnableDeveloper"
|
|
|
|
require.True(t, cloudRestricted(cfg, parseConfigPath(path)))
|
|
})
|
|
}
|
|
|
|
func TestSetConfigValue(t *testing.T) {
|
|
tests := map[string]struct {
|
|
path string
|
|
config *model.Config
|
|
args []string
|
|
expectedConfig *model.Config
|
|
}{
|
|
"bool": {
|
|
path: "LogSettings.EnableConsole",
|
|
args: []string{"true"},
|
|
config: &model.Config{LogSettings: model.LogSettings{
|
|
EnableConsole: model.NewPointer(false),
|
|
}},
|
|
expectedConfig: &model.Config{LogSettings: model.LogSettings{
|
|
EnableConsole: model.NewPointer(true),
|
|
}},
|
|
},
|
|
"string": {
|
|
path: "LogSettings.ConsoleLevel",
|
|
args: []string{"foo"},
|
|
config: &model.Config{LogSettings: model.LogSettings{
|
|
ConsoleLevel: model.NewPointer("ConsoleLevel"),
|
|
}},
|
|
expectedConfig: &model.Config{LogSettings: model.LogSettings{
|
|
ConsoleLevel: model.NewPointer("foo"),
|
|
}},
|
|
},
|
|
"int": {
|
|
path: "LogSettings.MaxFieldSize",
|
|
args: []string{"123"},
|
|
config: &model.Config{LogSettings: model.LogSettings{
|
|
MaxFieldSize: model.NewPointer(0),
|
|
}},
|
|
expectedConfig: &model.Config{LogSettings: model.LogSettings{
|
|
MaxFieldSize: model.NewPointer(123),
|
|
}},
|
|
},
|
|
"int64": {
|
|
path: "ServiceSettings.TLSStrictTransportMaxAge",
|
|
config: &model.Config{ServiceSettings: model.ServiceSettings{
|
|
TLSStrictTransportMaxAge: model.NewPointer(int64(0)),
|
|
}},
|
|
args: []string{"123"},
|
|
expectedConfig: &model.Config{ServiceSettings: model.ServiceSettings{
|
|
TLSStrictTransportMaxAge: model.NewPointer(int64(123)),
|
|
}},
|
|
},
|
|
"string slice": {
|
|
path: "SqlSettings.DataSourceReplicas",
|
|
args: []string{"abc", "def"},
|
|
config: &model.Config{SqlSettings: model.SqlSettings{
|
|
DataSourceReplicas: []string{},
|
|
}},
|
|
expectedConfig: &model.Config{SqlSettings: model.SqlSettings{
|
|
DataSourceReplicas: []string{"abc", "def"},
|
|
}},
|
|
},
|
|
"json.RawMessage": {
|
|
path: "LogSettings.AdvancedLoggingJSON",
|
|
config: &model.Config{LogSettings: model.LogSettings{
|
|
AdvancedLoggingJSON: nil,
|
|
}},
|
|
args: []string{`{"console1":{"Type":"console"}}`},
|
|
expectedConfig: &model.Config{LogSettings: model.LogSettings{
|
|
AdvancedLoggingJSON: json.RawMessage(`{"console1":{"Type":"console"}}`),
|
|
}},
|
|
},
|
|
}
|
|
|
|
for name, tc := range tests {
|
|
err := setConfigValue(parseConfigPath(tc.path), tc.config, tc.args)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, tc.expectedConfig, tc.config, name)
|
|
}
|
|
}
|
|
|
|
func (s *MmctlUnitTestSuite) TestConfigExportCmd() {
|
|
s.Run("Should get the config as-is", func() {
|
|
// there is not much to test as the config is returned as-is
|
|
// adding a test to make sure future changes are not breaking this
|
|
printer.Clean()
|
|
|
|
s.client.
|
|
EXPECT().
|
|
GetConfigWithOptions(context.TODO(), model.GetConfigOptions{}).
|
|
Return(map[string]any{
|
|
"SqlSettings": map[string]any{
|
|
"DriverName": "postgres",
|
|
},
|
|
}, &model.Response{}, nil).
|
|
Times(1)
|
|
|
|
err := configExportCmdF(s.client, &cobra.Command{}, nil)
|
|
s.Require().Nil(err)
|
|
s.Require().Len(printer.GetLines(), 1)
|
|
s.Require().Len(printer.GetErrorLines(), 0)
|
|
})
|
|
}
|