MM-12359 Add 'command show' CLI command (#10073)
* MM-12359 Add 'command show' CLI command This new CLI command prints out the detailed information of a given slash command. * Fix typo in error returned when moving slash commands * Move prettyPrint CLI functions to util * Move prettyPrint CLI tests to util
Этот коммит содержится в:
коммит произвёл
Jesús Espino
родитель
179e98c245
Коммит
58ef6853f1
@@ -28,6 +28,15 @@ var CommandCreateCmd = &cobra.Command{
|
|||||||
RunE: createCommandCmdF,
|
RunE: createCommandCmdF,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var CommandShowCmd = &cobra.Command{
|
||||||
|
Use: "show",
|
||||||
|
Short: "Show a custom slash command",
|
||||||
|
Long: `Show a custom slash command. Commands can be specified by command ID.`,
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Example: ` command show commandID`,
|
||||||
|
RunE: showCommandCmdF,
|
||||||
|
}
|
||||||
|
|
||||||
var CommandMoveCmd = &cobra.Command{
|
var CommandMoveCmd = &cobra.Command{
|
||||||
Use: "move",
|
Use: "move",
|
||||||
Short: "Move a slash command to a different team",
|
Short: "Move a slash command to a different team",
|
||||||
@@ -71,6 +80,7 @@ func init() {
|
|||||||
|
|
||||||
CommandCmd.AddCommand(
|
CommandCmd.AddCommand(
|
||||||
CommandCreateCmd,
|
CommandCreateCmd,
|
||||||
|
CommandShowCmd,
|
||||||
CommandMoveCmd,
|
CommandMoveCmd,
|
||||||
CommandListCmd,
|
CommandListCmd,
|
||||||
CommandDeleteCmd,
|
CommandDeleteCmd,
|
||||||
@@ -148,6 +158,24 @@ func createCommandCmdF(command *cobra.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func showCommandCmdF(command *cobra.Command, args []string) error {
|
||||||
|
a, err := InitDBCommandContextCobra(command)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer a.Shutdown()
|
||||||
|
|
||||||
|
slashCommand := getCommandFromCommandArg(a, args[0])
|
||||||
|
if slashCommand == nil {
|
||||||
|
command.SilenceUsage = true
|
||||||
|
return errors.New("Unable to find command '" + args[0] + "'")
|
||||||
|
}
|
||||||
|
// pretty print
|
||||||
|
fmt.Printf("%s", prettyPrintStruct(*slashCommand))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func moveCommandCmdF(command *cobra.Command, args []string) error {
|
func moveCommandCmdF(command *cobra.Command, args []string) error {
|
||||||
a, err := InitDBCommandContextCobra(command)
|
a, err := InitDBCommandContextCobra(command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -156,7 +184,7 @@ func moveCommandCmdF(command *cobra.Command, args []string) error {
|
|||||||
defer a.Shutdown()
|
defer a.Shutdown()
|
||||||
|
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return errors.New("Enter the destination team and at least one comamnd to move.")
|
return errors.New("Enter the destination team and at least one command to move.")
|
||||||
}
|
}
|
||||||
|
|
||||||
team := getTeamFromTeamArg(a, args[0])
|
team := getTeamFromTeamArg(a, args[0])
|
||||||
|
|||||||
@@ -131,6 +131,51 @@ func TestCreateCommand(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestShowCommand(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
url := "http://localhost:8000/test-command"
|
||||||
|
team := th.BasicTeam
|
||||||
|
user := th.BasicUser
|
||||||
|
th.LinkUserToTeam(user, team)
|
||||||
|
trigger := "trigger_" + model.NewId()
|
||||||
|
displayName := "dn_" + model.NewId()
|
||||||
|
|
||||||
|
c := &model.Command{
|
||||||
|
DisplayName: displayName,
|
||||||
|
Method: "G",
|
||||||
|
TeamId: team.Id,
|
||||||
|
Username: user.Username,
|
||||||
|
CreatorId: user.Id,
|
||||||
|
URL: url,
|
||||||
|
Trigger: trigger,
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("existing command", func(t *testing.T) {
|
||||||
|
command, err := th.App.CreateCommand(c)
|
||||||
|
require.Nil(t, err)
|
||||||
|
commands, err := th.App.ListTeamCommands(team.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
assert.Equal(t, len(commands), 1)
|
||||||
|
|
||||||
|
output := th.CheckCommand(t, "command", "show", command.Id)
|
||||||
|
assert.Contains(t, string(output), command.Id)
|
||||||
|
assert.Contains(t, string(output), command.TeamId)
|
||||||
|
assert.Contains(t, string(output), trigger)
|
||||||
|
assert.Contains(t, string(output), displayName)
|
||||||
|
assert.Contains(t, string(output), user.Username)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("not existing command", func(t *testing.T) {
|
||||||
|
assert.Error(t, th.RunCommand(t, "command", "show", "invalid"))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("no commandID", func(t *testing.T) {
|
||||||
|
assert.Error(t, th.RunCommand(t, "command", "show"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestDeleteCommand(t *testing.T) {
|
func TestDeleteCommand(t *testing.T) {
|
||||||
// Skipped due to v5.6 RC build issues.
|
// Skipped due to v5.6 RC build issues.
|
||||||
t.Skip()
|
t.Skip()
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -15,7 +14,6 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/mlog"
|
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
"github.com/mattermost/mattermost-server/utils"
|
"github.com/mattermost/mattermost-server/utils"
|
||||||
"github.com/mattermost/mattermost-server/utils/fileutils"
|
"github.com/mattermost/mattermost-server/utils/fileutils"
|
||||||
@@ -178,7 +176,7 @@ func configShowCmdF(command *cobra.Command, args []string) error {
|
|||||||
config := app.Config()
|
config := app.Config()
|
||||||
|
|
||||||
// pretty print
|
// pretty print
|
||||||
fmt.Printf("%s", prettyPrint(configToMap(*config)))
|
fmt.Printf("%s", prettyPrintStruct(*config))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,37 +203,6 @@ func printConfigValues(configMap map[string]interface{}, configSetting []string,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// prettyPrint the map
|
|
||||||
func prettyPrint(configMap map[string]interface{}) string {
|
|
||||||
value := reflect.ValueOf(configMap)
|
|
||||||
return printMap(value, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// printMap takes a reflect.Value and print it out, recursively if its a map with the given tab settings.
|
|
||||||
func printMap(value reflect.Value, tabVal int) string {
|
|
||||||
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
|
|
||||||
for _, key := range value.MapKeys() {
|
|
||||||
val := value.MapIndex(key)
|
|
||||||
if newVal, ok := val.Interface().(map[string]interface{}); !ok {
|
|
||||||
fmt.Fprintf(out, "%s", strings.Repeat("\t", tabVal))
|
|
||||||
fmt.Fprintf(out, "%v: \"%v\"\n", key.Interface(), val.Interface())
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(out, "%s", strings.Repeat("\t", tabVal))
|
|
||||||
fmt.Fprintf(out, "%v:\n", key.Interface())
|
|
||||||
// going one level in, increase the tab
|
|
||||||
tabVal++
|
|
||||||
fmt.Fprintf(out, "%s", printMap(reflect.ValueOf(newVal), tabVal))
|
|
||||||
// coming back one level, decrease the tab
|
|
||||||
tabVal--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return out.String()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func configSetCmdF(command *cobra.Command, args []string) error {
|
func configSetCmdF(command *cobra.Command, args []string) error {
|
||||||
app, err := InitDBCommandContextCobra(command)
|
app, err := InitDBCommandContextCobra(command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -388,44 +355,3 @@ func UpdateMap(configMap map[string]interface{}, configSettings []string, newVal
|
|||||||
func configToMap(s interface{}) map[string]interface{} {
|
func configToMap(s interface{}) map[string]interface{} {
|
||||||
return structToMap(s)
|
return structToMap(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// structToMap converts a struct into a map
|
|
||||||
func structToMap(t interface{}) map[string]interface{} {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
mlog.Error(fmt.Sprintf("Panicked in structToMap. This should never happen. %v", r))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
val := reflect.ValueOf(t)
|
|
||||||
|
|
||||||
if val.Kind() != reflect.Struct {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
out := map[string]interface{}{}
|
|
||||||
|
|
||||||
for i := 0; i < val.NumField(); i++ {
|
|
||||||
field := val.Field(i)
|
|
||||||
|
|
||||||
var value interface{}
|
|
||||||
|
|
||||||
switch field.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
value = structToMap(field.Interface())
|
|
||||||
case reflect.Ptr:
|
|
||||||
indirectType := field.Elem()
|
|
||||||
|
|
||||||
if indirectType.Kind() == reflect.Struct {
|
|
||||||
value = structToMap(indirectType.Interface())
|
|
||||||
} else {
|
|
||||||
value = indirectType.Interface()
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
value = field.Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
out[val.Type().Field(i).Name] = value
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -151,93 +151,8 @@ func TestConfigSet(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStructToMap(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
Name string
|
|
||||||
Input interface{}
|
|
||||||
Expected map[string]interface{}
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Name: "Struct with one string field",
|
|
||||||
Input: struct {
|
|
||||||
Test string
|
|
||||||
}{
|
|
||||||
Test: "test",
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"Test": "test",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "String with multiple fields of different ",
|
|
||||||
Input: struct {
|
|
||||||
Test1 string
|
|
||||||
Test2 int
|
|
||||||
Test3 string
|
|
||||||
Test4 bool
|
|
||||||
}{
|
|
||||||
Test1: "test1",
|
|
||||||
Test2: 21,
|
|
||||||
Test3: "test2",
|
|
||||||
Test4: false,
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"Test1": "test1",
|
|
||||||
"Test2": 21,
|
|
||||||
"Test3": "test2",
|
|
||||||
"Test4": false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Nested fields",
|
|
||||||
Input: TestConfig{
|
|
||||||
TestServiceSettings{"abc", "def", "ghi"},
|
|
||||||
TestTeamSettings{"abc", 1},
|
|
||||||
TestClientRequirements{"abc", "def", "ghi"},
|
|
||||||
TestMessageExportSettings{true, "abc", TestGlobalRelaySettings{"abc", "def", "ghi"}},
|
|
||||||
},
|
|
||||||
Expected: map[string]interface{}{
|
|
||||||
"TestServiceSettings": map[string]interface{}{
|
|
||||||
"Siteurl": "abc",
|
|
||||||
"Websocketurl": "def",
|
|
||||||
"Licensedfieldlocation": "ghi",
|
|
||||||
},
|
|
||||||
"TestTeamSettings": map[string]interface{}{
|
|
||||||
"Sitename": "abc",
|
|
||||||
"Maxuserperteam": 1,
|
|
||||||
},
|
|
||||||
"TestClientRequirements": map[string]interface{}{
|
|
||||||
"Androidlatestversion": "abc",
|
|
||||||
"Androidminversion": "def",
|
|
||||||
"Desktoplatestversion": "ghi",
|
|
||||||
},
|
|
||||||
"TestMessageExportSettings": map[string]interface{}{
|
|
||||||
"Enableexport": true,
|
|
||||||
"Exportformat": "abc",
|
|
||||||
"TestGlobalRelaySettings": map[string]interface{}{
|
|
||||||
"Customertype": "abc",
|
|
||||||
"Smtpusername": "def",
|
|
||||||
"Smtppassword": "ghi",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range cases {
|
|
||||||
t.Run(test.Name, func(t *testing.T) {
|
|
||||||
res := structToMap(test.Input)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(res, test.Expected) {
|
|
||||||
t.Errorf("got %v want %v ", res, test.Expected)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfigToMap(t *testing.T) {
|
func TestConfigToMap(t *testing.T) {
|
||||||
// This test is almost the same as TestMapToStruct, but I have it here for the sake of completions
|
// This test is almost the same as TestStructToMap, but I have it here for the sake of completions
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Name string
|
Name string
|
||||||
Input interface{}
|
Input interface{}
|
||||||
@@ -321,68 +236,6 @@ func TestConfigToMap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrintMap(t *testing.T) {
|
|
||||||
inputCases := []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"CustomerType": "A9",
|
|
||||||
"SmtpUsername": "",
|
|
||||||
"SmtpPassword": "",
|
|
||||||
"EmailAddress": "",
|
|
||||||
},
|
|
||||||
map[string]interface{}{
|
|
||||||
"EnableExport": false,
|
|
||||||
"ExportFormat": "actiance",
|
|
||||||
"DailyRunTime": "01:00",
|
|
||||||
"GlobalRelaySettings": map[string]interface{}{
|
|
||||||
"CustomerType": "A9",
|
|
||||||
"SmtpUsername": "",
|
|
||||||
"SmtpPassword": "",
|
|
||||||
"EmailAddress": "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
outputCases := []string{
|
|
||||||
"CustomerType: \"A9\"\nSmtpUsername: \"\"\nSmtpPassword: \"\"\nEmailAddress: \"\"\n",
|
|
||||||
"EnableExport: \"false\"\nExportFormat: \"actiance\"\nDailyRunTime: \"01:00\"\nGlobalRelaySettings:\n\t CustomerType: \"A9\"\n\tSmtpUsername: \"\"\n\tSmtpPassword: \"\"\n\tEmailAddress: \"\"\n",
|
|
||||||
}
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
Name string
|
|
||||||
Input reflect.Value
|
|
||||||
Expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Name: "Basic print",
|
|
||||||
Input: reflect.ValueOf(inputCases[0]),
|
|
||||||
Expected: outputCases[0],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Complex print",
|
|
||||||
Input: reflect.ValueOf(inputCases[1]),
|
|
||||||
Expected: outputCases[1],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range cases {
|
|
||||||
t.Run(test.Name, func(t *testing.T) {
|
|
||||||
res := printMap(test.Input, 0)
|
|
||||||
|
|
||||||
// create two slice of string formed by splitting our strings on \n
|
|
||||||
slice1 := strings.Split(res, "\n")
|
|
||||||
slice2 := strings.Split(res, "\n")
|
|
||||||
|
|
||||||
sort.Strings(slice1)
|
|
||||||
sort.Strings(slice2)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(slice1, slice2) {
|
|
||||||
t.Errorf("got '%#v' want '%#v", slice1, slice2)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrintConfigValues(t *testing.T) {
|
func TestPrintConfigValues(t *testing.T) {
|
||||||
outputs := []string{
|
outputs := []string{
|
||||||
"Siteurl: \"abc\"\nWebsocketurl: \"def\"\nLicensedfieldlocation: \"ghi\"\n",
|
"Siteurl: \"abc\"\nWebsocketurl: \"def\"\nLicensedfieldlocation: \"ghi\"\n",
|
||||||
|
|||||||
89
cmd/mattermost/commands/utils.go
Обычный файл
89
cmd/mattermost/commands/utils.go
Обычный файл
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright (c) 2019-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/mlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// prettyPrintStruct will return a prettyPrint version of a given struct
|
||||||
|
func prettyPrintStruct(t interface{}) string {
|
||||||
|
return prettyPrintMap(structToMap(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
// structToMap converts a struct into a map
|
||||||
|
func structToMap(t interface{}) map[string]interface{} {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
mlog.Error(fmt.Sprintf("Panicked in structToMap. This should never happen. %v", r))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
val := reflect.ValueOf(t)
|
||||||
|
|
||||||
|
if val.Kind() != reflect.Struct {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
out := map[string]interface{}{}
|
||||||
|
|
||||||
|
for i := 0; i < val.NumField(); i++ {
|
||||||
|
field := val.Field(i)
|
||||||
|
|
||||||
|
var value interface{}
|
||||||
|
|
||||||
|
switch field.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
value = structToMap(field.Interface())
|
||||||
|
case reflect.Ptr:
|
||||||
|
indirectType := field.Elem()
|
||||||
|
|
||||||
|
if indirectType.Kind() == reflect.Struct {
|
||||||
|
value = structToMap(indirectType.Interface())
|
||||||
|
} else {
|
||||||
|
value = indirectType.Interface()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
value = field.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
out[val.Type().Field(i).Name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// prettyPrintMap will return a prettyPrint version of a given map
|
||||||
|
func prettyPrintMap(configMap map[string]interface{}) string {
|
||||||
|
value := reflect.ValueOf(configMap)
|
||||||
|
return printMap(value, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// printMap takes a reflect.Value and prints it out, recursively if it's a map with the given tab settings
|
||||||
|
func printMap(value reflect.Value, tabVal int) string {
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
|
||||||
|
for _, key := range value.MapKeys() {
|
||||||
|
val := value.MapIndex(key)
|
||||||
|
if newVal, ok := val.Interface().(map[string]interface{}); !ok {
|
||||||
|
fmt.Fprintf(out, "%s", strings.Repeat("\t", tabVal))
|
||||||
|
fmt.Fprintf(out, "%v: \"%v\"\n", key.Interface(), val.Interface())
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(out, "%s", strings.Repeat("\t", tabVal))
|
||||||
|
fmt.Fprintf(out, "%v:\n", key.Interface())
|
||||||
|
// going one level in, increase the tab
|
||||||
|
tabVal++
|
||||||
|
fmt.Fprintf(out, "%s", printMap(reflect.ValueOf(newVal), tabVal))
|
||||||
|
// coming back one level, decrease the tab
|
||||||
|
tabVal--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.String()
|
||||||
|
}
|
||||||
157
cmd/mattermost/commands/utils_test.go
Обычный файл
157
cmd/mattermost/commands/utils_test.go
Обычный файл
@@ -0,0 +1,157 @@
|
|||||||
|
// Copyright (c) 2019-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStructToMap(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
Name string
|
||||||
|
Input interface{}
|
||||||
|
Expected map[string]interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "Struct with one string field",
|
||||||
|
Input: struct {
|
||||||
|
Test string
|
||||||
|
}{
|
||||||
|
Test: "test",
|
||||||
|
},
|
||||||
|
Expected: map[string]interface{}{
|
||||||
|
"Test": "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "String with multiple fields of different ",
|
||||||
|
Input: struct {
|
||||||
|
Test1 string
|
||||||
|
Test2 int
|
||||||
|
Test3 string
|
||||||
|
Test4 bool
|
||||||
|
}{
|
||||||
|
Test1: "test1",
|
||||||
|
Test2: 21,
|
||||||
|
Test3: "test2",
|
||||||
|
Test4: false,
|
||||||
|
},
|
||||||
|
Expected: map[string]interface{}{
|
||||||
|
"Test1": "test1",
|
||||||
|
"Test2": 21,
|
||||||
|
"Test3": "test2",
|
||||||
|
"Test4": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Nested fields",
|
||||||
|
Input: TestConfig{
|
||||||
|
TestServiceSettings{"abc", "def", "ghi"},
|
||||||
|
TestTeamSettings{"abc", 1},
|
||||||
|
TestClientRequirements{"abc", "def", "ghi"},
|
||||||
|
TestMessageExportSettings{true, "abc", TestGlobalRelaySettings{"abc", "def", "ghi"}},
|
||||||
|
},
|
||||||
|
Expected: map[string]interface{}{
|
||||||
|
"TestServiceSettings": map[string]interface{}{
|
||||||
|
"Siteurl": "abc",
|
||||||
|
"Websocketurl": "def",
|
||||||
|
"Licensedfieldlocation": "ghi",
|
||||||
|
},
|
||||||
|
"TestTeamSettings": map[string]interface{}{
|
||||||
|
"Sitename": "abc",
|
||||||
|
"Maxuserperteam": 1,
|
||||||
|
},
|
||||||
|
"TestClientRequirements": map[string]interface{}{
|
||||||
|
"Androidlatestversion": "abc",
|
||||||
|
"Androidminversion": "def",
|
||||||
|
"Desktoplatestversion": "ghi",
|
||||||
|
},
|
||||||
|
"TestMessageExportSettings": map[string]interface{}{
|
||||||
|
"Enableexport": true,
|
||||||
|
"Exportformat": "abc",
|
||||||
|
"TestGlobalRelaySettings": map[string]interface{}{
|
||||||
|
"Customertype": "abc",
|
||||||
|
"Smtpusername": "def",
|
||||||
|
"Smtppassword": "ghi",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range cases {
|
||||||
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
|
res := structToMap(test.Input)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(res, test.Expected) {
|
||||||
|
t.Errorf("got %v want %v ", res, test.Expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrintMap(t *testing.T) {
|
||||||
|
inputCases := []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"CustomerType": "A9",
|
||||||
|
"SmtpUsername": "",
|
||||||
|
"SmtpPassword": "",
|
||||||
|
"EmailAddress": "",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"EnableExport": false,
|
||||||
|
"ExportFormat": "actiance",
|
||||||
|
"DailyRunTime": "01:00",
|
||||||
|
"GlobalRelaySettings": map[string]interface{}{
|
||||||
|
"CustomerType": "A9",
|
||||||
|
"SmtpUsername": "",
|
||||||
|
"SmtpPassword": "",
|
||||||
|
"EmailAddress": "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
outputCases := []string{
|
||||||
|
"CustomerType: \"A9\"\nSmtpUsername: \"\"\nSmtpPassword: \"\"\nEmailAddress: \"\"\n",
|
||||||
|
"EnableExport: \"false\"\nExportFormat: \"actiance\"\nDailyRunTime: \"01:00\"\nGlobalRelaySettings:\n\t CustomerType: \"A9\"\n\tSmtpUsername: \"\"\n\tSmtpPassword: \"\"\n\tEmailAddress: \"\"\n",
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
Name string
|
||||||
|
Input reflect.Value
|
||||||
|
Expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "Basic print",
|
||||||
|
Input: reflect.ValueOf(inputCases[0]),
|
||||||
|
Expected: outputCases[0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Complex print",
|
||||||
|
Input: reflect.ValueOf(inputCases[1]),
|
||||||
|
Expected: outputCases[1],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range cases {
|
||||||
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
|
res := printMap(test.Input, 0)
|
||||||
|
|
||||||
|
// create two slice of string formed by splitting our strings on \n
|
||||||
|
slice1 := strings.Split(res, "\n")
|
||||||
|
slice2 := strings.Split(res, "\n")
|
||||||
|
|
||||||
|
sort.Strings(slice1)
|
||||||
|
sort.Strings(slice2)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(slice1, slice2) {
|
||||||
|
t.Errorf("got '%#v' want '%#v", slice1, slice2)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Ссылка в новой задаче
Block a user