Files
zeekay 8feb24ea1f One name for this fork, and it is ours
The server module named github.com/hanzoai/team/server/v8 — a path that
resolves to the retired workspace's repository, not this one — while
server/public still published github.com/mattermost/mattermost/server/public.
Both are github.com/hanzoteam/server/... now, which is where this code
actually lives.

That second half is what made the binary unbootable. Upstream's public path
stayed claimed by upstream, mattermost-plugin-ai imported it, and the build
linked public/model twice; public/plugin's gob.Register then ran twice under
one name and panicked at init, before main. Reverting the rename traded the
brand for a working binary. Rebranding the plugin alongside it needs no such
trade: the agents fork is github.com/hanzoteam/agents/v2 against our public
module, so no module named mattermost/mattermost is in the graph at all.

Measured: `go list -deps ./cmd/mattermost` names exactly one public/model, and
it is ours.
2026-08-28 23:35:36 -07:00

3555 lines
104 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package commands
import (
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/hanzoteam/server/server/public/model"
"github.com/hanzoteam/server/server/v8/cmd/mmctl/printer"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func (s *MmctlUnitTestSuite) TestUserActivateCmd() {
s.Run("Activate user", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "example", Username: "ExampleUser", Email: emailArg}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser.Id, true).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := userActivateCmdF(s.client, &cobra.Command{}, []string{emailArg})
s.Require().NoError(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Try to activate unexistent user", func() {
printer.Clean()
emailArg := "[email protected]"
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(nil, &model.Response{StatusCode: http.StatusNotFound}, errors.New("mock error")).
Times(1)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), emailArg, "").
Return(nil, &model.Response{StatusCode: http.StatusNotFound}, errors.New("mock error")).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), emailArg, "").
Return(nil, &model.Response{StatusCode: http.StatusNotFound}, errors.New("mock error")).
Times(1)
err := userActivateCmdF(s.client, &cobra.Command{}, []string{emailArg})
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 1)
s.Require().Equal(fmt.Sprintf("1 error occurred:\n\t* user %s not found\n\n", emailArg), printer.GetErrorLines()[0])
})
s.Run("Fail to activate user", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "example", Username: "ExampleUser", Email: emailArg}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser.Id, true).
Return(&model.Response{StatusCode: http.StatusBadRequest}, errors.New("mock error")).
Times(1)
err := userActivateCmdF(s.client, &cobra.Command{}, []string{emailArg})
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 1)
s.Require().Equal(fmt.Sprintf("unable to change activation status of user %v: mock error", mockUser.Id), printer.GetErrorLines()[0])
})
s.Run("Activate several users with unexistent ones and failed ones", func() {
printer.Clean()
emailArgs := []string{"[email protected]", "null", "[email protected]", "[email protected]", "[email protected]"}
mockUser0 := model.User{Id: "example0", Username: "ExampleUser0", Email: emailArgs[0]}
mockUser2 := model.User{Id: "example2", AuthService: "other", Username: "ExampleUser2", Email: emailArgs[2]}
mockUser3 := model.User{Id: "failure", Username: "FailureUser", Email: emailArgs[3]}
mockUser4 := model.User{Id: "example4", Username: "ExampleUser4", Email: emailArgs[4]}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArgs[0], "").
Return(&mockUser0, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), emailArgs[1], "").
Return(nil, &model.Response{StatusCode: http.StatusNotFound}, errors.New("mock error")).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), emailArgs[1], "").
Return(nil, &model.Response{StatusCode: http.StatusNotFound}, errors.New("mock error")).
Times(1)
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArgs[2], "").
Return(&mockUser2, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArgs[3], "").
Return(&mockUser3, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArgs[4], "").
Return(&mockUser4, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser0.Id, true).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser2.Id, true).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser3.Id, true).
Return(&model.Response{StatusCode: http.StatusBadRequest}, errors.New("mock error")).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser4.Id, true).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := userActivateCmdF(s.client, &cobra.Command{}, emailArgs)
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 2)
s.Require().Equal(fmt.Sprintf("1 error occurred:\n\t* user %s not found\n\n", emailArgs[1]), printer.GetErrorLines()[0])
s.Require().Equal(fmt.Sprintf("unable to change activation status of user %v: mock error", mockUser3.Id), printer.GetErrorLines()[1])
})
s.Run("shell completion", func() {
s.Run("one element matches", func() {
mockUsers := []*model.User{
{
Id: "0_id",
Username: "0_username",
Email: "[email protected]",
},
{
Id: "1_id",
Username: "1_username",
Email: "[email protected]",
},
{
Id: "2_id",
Username: "2_username",
Email: "[email protected]",
},
}
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.Background(), 0, DefaultPageSize, "inactive=true", "").
Return(mockUsers, &model.Response{}, nil).
Times(1)
r, dir := userActivateCompletionF(context.Background(), s.client, nil, nil, "1")
s.Equal(cobra.ShellCompDirectiveNoFileComp, dir)
s.Equal([]string{"1_id"}, r)
})
})
}
func (s *MmctlUnitTestSuite) TestDeactivateUserCmd() {
s.Run("Deactivate user", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "example", Username: "ExampleUser", Email: emailArg}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser.Id, false).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := userDeactivateCmdF(s.client, &cobra.Command{}, []string{emailArg})
s.Require().NoError(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Try to deactivate unexistent user", func() {
printer.Clean()
emailArg := "[email protected]"
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(nil, &model.Response{StatusCode: http.StatusNotFound}, errors.New("mock error")).
Times(1)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), emailArg, "").
Return(nil, &model.Response{StatusCode: http.StatusNotFound}, errors.New("mock error")).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), emailArg, "").
Return(nil, &model.Response{StatusCode: http.StatusBadRequest}, errors.New("mock error")).
Times(1)
err := userDeactivateCmdF(s.client, &cobra.Command{}, []string{emailArg})
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 1)
s.Require().Equal(fmt.Sprintf("1 error occurred:\n\t* user %v not found\n\n", emailArg), printer.GetErrorLines()[0])
})
s.Run("Fail to deactivate user", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "example", Username: "ExampleUser", Email: emailArg}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser.Id, false).
Return(&model.Response{StatusCode: http.StatusBadRequest}, errors.New("mock error")).
Times(1)
err := userDeactivateCmdF(s.client, &cobra.Command{}, []string{emailArg})
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 1)
s.Require().Equal(fmt.Sprintf("unable to change activation status of user %v: mock error", mockUser.Id), printer.GetErrorLines()[0])
})
s.Run("Deactivate SSO user", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "example", AuthService: "other", Username: "ExampleUser", Email: emailArg}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser.Id, false).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := userDeactivateCmdF(s.client, &cobra.Command{}, []string{emailArg})
s.Require().NoError(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal("You must also deactivate user "+mockUser.Id+" in the SSO provider or they will be reactivated on next login or sync.", printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Deactivate several users with unexistent ones, SSO ones and failed ones", func() {
printer.Clean()
emailArgs := []string{"[email protected]", "null", "[email protected]", "[email protected]", "[email protected]"}
mockUser0 := model.User{Id: "example0", Username: "ExampleUser0", Email: emailArgs[0]}
mockUser2 := model.User{Id: "example2", AuthService: "other", Username: "ExampleUser2", Email: emailArgs[2]}
mockUser3 := model.User{Id: "failure", Username: "FailureUser", Email: emailArgs[3]}
mockUser4 := model.User{Id: "example4", Username: "ExampleUser4", Email: emailArgs[4]}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArgs[0], "").
Return(&mockUser0, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), emailArgs[1], "").
Return(nil, &model.Response{StatusCode: http.StatusNotFound}, errors.New("mock error")).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), emailArgs[1], "").
Return(nil, &model.Response{StatusCode: http.StatusNotFound}, errors.New("mock error")).
Times(1)
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArgs[2], "").
Return(&mockUser2, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArgs[3], "").
Return(&mockUser3, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArgs[4], "").
Return(&mockUser4, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser0.Id, false).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser2.Id, false).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser3.Id, false).
Return(&model.Response{StatusCode: http.StatusBadRequest}, errors.New("mock error")).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser4.Id, false).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := userDeactivateCmdF(s.client, &cobra.Command{}, emailArgs)
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal("You must also deactivate user "+mockUser2.Id+" in the SSO provider or they will be reactivated on next login or sync.", printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 2)
s.Require().Equal(fmt.Sprintf("1 error occurred:\n\t* user %v not found\n\n", emailArgs[1]), printer.GetErrorLines()[0])
s.Require().Equal(fmt.Sprintf("unable to change activation status of user %v: mock error", mockUser3.Id), printer.GetErrorLines()[1])
})
}
func (s *MmctlUnitTestSuite) TestDeleteUsersCmd() {
email1 := "[email protected]"
email2 := "[email protected]"
userID1 := model.NewId()
userID2 := model.NewId()
mockUser1 := model.User{Username: "User1", Email: email1, Id: userID1}
mockUser2 := model.User{Username: "User2", Email: email2, Id: userID2}
s.Run("Delete users with confirm false returns an error", func() {
cmd := &cobra.Command{}
cmd.Flags().Bool("confirm", false, "")
err := deleteUsersCmdF(s.client, cmd, []string{"some"})
s.Require().NotNil(err)
s.Require().Equal("could not proceed, either enable --confirm flag or use an interactive shell to complete operation: this is not an interactive shell", err.Error())
})
s.Run("Delete user that does not exist in db returns an error", func() {
printer.Clean()
arg := "[email protected]"
var expectedErr *multierror.Error
expectedErr = multierror.Append(expectedErr, fmt.Errorf("user %s not found", arg))
s.client.
EXPECT().
GetUserByEmail(context.TODO(), arg, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), arg, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), arg, "").
Return(nil, &model.Response{}, nil).
Times(1)
cmd := &cobra.Command{}
cmd.Flags().Bool("confirm", true, "")
err := deleteUsersCmdF(s.client, cmd, []string{arg})
s.Require().Error(err)
s.Require().EqualError(err, expectedErr.Error())
})
s.Run("Delete users should delete users", func() {
printer.Clean()
s.client.
EXPECT().
GetUserByEmail(context.TODO(), email1, "").
Return(&mockUser1, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
PermanentDeleteUser(context.TODO(), userID1).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
s.client.
EXPECT().
GetUserByEmail(context.TODO(), email2, "").
Return(&mockUser2, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
PermanentDeleteUser(context.TODO(), userID2).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
cmd := &cobra.Command{}
cmd.Flags().Bool("confirm", true, "")
err := deleteUsersCmdF(s.client, cmd, []string{email1, email2})
s.Require().Nil(err)
s.Require().Equal(&mockUser1, printer.GetLines()[0])
s.Require().Equal(&mockUser2, printer.GetLines()[1])
})
s.Run("Delete users with error on PermanentDeleteUser returns an error", func() {
printer.Clean()
mockError := errors.New("an error occurred on deleting a user")
var expectedErr *multierror.Error
expectedErr = multierror.Append(expectedErr, fmt.Errorf("unable to delete user %s error: %w", mockUser1.Username, mockError))
s.client.
EXPECT().
GetUserByEmail(context.TODO(), email1, "").
Return(&mockUser1, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
PermanentDeleteUser(context.TODO(), userID1).
Return(&model.Response{StatusCode: http.StatusBadRequest}, mockError).
Times(1)
cmd := &cobra.Command{}
cmd.Flags().Bool("confirm", true, "")
err := deleteUsersCmdF(s.client, cmd, []string{email1})
s.Require().EqualError(err, expectedErr.Error())
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Delete two users, first fails with error other passes", func() {
printer.Clean()
mockError := errors.New("an error occurred on deleting a user")
var expectedErr *multierror.Error
expectedErr = multierror.Append(expectedErr, fmt.Errorf("unable to delete user %s error: %w", mockUser1.Username, mockError))
s.client.
EXPECT().
GetUserByEmail(context.TODO(), email1, "").
Return(&mockUser1, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByEmail(context.TODO(), email2, "").
Return(&mockUser2, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
PermanentDeleteUser(context.TODO(), userID1).
Return(&model.Response{StatusCode: http.StatusBadRequest}, mockError).
Times(1)
s.client.
EXPECT().
PermanentDeleteUser(context.TODO(), userID2).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
cmd := &cobra.Command{}
cmd.Flags().Bool("confirm", true, "")
err := deleteUsersCmdF(s.client, cmd, []string{email1, email2})
s.Require().NotNil(err)
s.Require().EqualError(err, expectedErr.Error())
s.Require().Equal(&mockUser2, printer.GetLines()[0])
})
s.Run("partial delete of user, i.e failing to delete profile image gives a warning on the console.", func() {
printer.Clean()
var expectedErr *multierror.Error
expectedErr = multierror.Append(expectedErr,
fmt.Errorf("unable to delete the profile image of the user, please delete it manually, id:%s", mockUser1.Username))
s.client.
EXPECT().
GetUserByEmail(context.TODO(), email1, "").
Return(&mockUser1, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
PermanentDeleteUser(context.TODO(), userID1).
Return(&model.Response{StatusCode: http.StatusAccepted}, nil).
Times(1)
cmd := &cobra.Command{}
cmd.Flags().Bool("confirm", true, "")
err := deleteUsersCmdF(s.client, cmd, []string{email1})
s.Require().NotNil(err)
s.Require().EqualError(err, expectedErr.Error())
})
}
func (s *MmctlUnitTestSuite) TestSearchUserCmd() {
s.Run("Search for an existing user", func() {
emailArg := "[email protected]"
mockUser := &model.User{Username: "ExampleUser", Email: emailArg}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(mockUser, &model.Response{}, nil).
Times(1)
err := searchUserCmdF(s.client, &cobra.Command{}, []string{emailArg})
s.Require().Nil(err)
s.Require().Equal(userOut{User: mockUser, Deactivated: false}, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Search for a disabled user", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := &model.User{Username: "ExampleUser", Email: emailArg, DeleteAt: 1234}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(mockUser, &model.Response{}, nil).
Times(1)
err := searchUserCmdF(s.client, &cobra.Command{}, []string{emailArg})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(userOut{User: mockUser, Deactivated: true}, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Search for a user with authData", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := &model.User{Username: "ExampleUser", Email: emailArg, AuthData: new("1234"), AuthService: model.UserAuthServiceLdap}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(mockUser, &model.Response{}, nil).
Times(1)
err := searchUserCmdF(s.client, &cobra.Command{}, []string{emailArg})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(userOut{User: mockUser, Deactivated: false, AuthData: "1234"}, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Plain output includes the user's roles", func() {
printer.Clean()
printer.SetFormat(printer.FormatPlain)
defer printer.SetFormat(printer.FormatJSON)
emailArg := "[email protected]"
mockUser := &model.User{Username: "ExampleUser", Email: emailArg, Roles: "system_user system_admin"}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(mockUser, &model.Response{}, nil).
Times(1)
err := searchUserCmdF(s.client, &cobra.Command{}, []string{emailArg})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Len(printer.GetErrorLines(), 0)
s.Require().Contains(printer.GetLines()[0], "roles: system_user system_admin")
})
s.Run("Search for a nonexistent user", func() {
printer.Clean()
arg := "[email protected]"
s.client.
EXPECT().
GetUserByEmail(context.TODO(), arg, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), arg, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), arg, "").
Return(nil, &model.Response{}, nil).
Times(1)
err := searchUserCmdF(s.client, &cobra.Command{}, []string{arg})
s.Require().NotNil(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Equal("1 error occurred:\n\t* user [email protected] not found\n\n", printer.GetErrorLines()[0])
})
s.Run("Avoid path traversal", func() {
printer.Clean()
arg := "test/../[email protected]"
err := searchUserCmdF(s.client, &cobra.Command{}, []string{arg})
s.Require().NotNil(err)
s.Require().Equal("1 error occurred:\n\t* user test/../[email protected] not found\n\n", printer.GetErrorLines()[0])
})
s.Run("Error while getting user by email", func() {
printer.Clean()
emailArg := "[email protected]"
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(nil, &model.Response{}, errors.New("Error while getting user by email")).
Times(1)
err := searchUserCmdF(s.client, &cobra.Command{}, []string{emailArg})
s.Require().NotNil(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Equal("1 error occurred:\n\t* Error while getting user by email\n\n", err.Error())
s.Require().Equal("1 error occurred:\n\t* Error while getting user by email\n\n", printer.GetErrorLines()[0])
})
s.Run("Error while getting user by username", func() {
printer.Clean()
usernameArg := "exampleUser"
s.client.
EXPECT().
GetUserByUsername(context.TODO(), usernameArg, "").
Return(nil, &model.Response{}, errors.New("Error while getting user by username")).
Times(1)
err := searchUserCmdF(s.client, &cobra.Command{}, []string{usernameArg})
s.Require().NotNil(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Equal("1 error occurred:\n\t* Error while getting user by username\n\n", err.Error())
s.Require().Equal("1 error occurred:\n\t* Error while getting user by username\n\n", printer.GetErrorLines()[0])
})
s.Run("Error while getting user", func() {
printer.Clean()
userArg := "exampleUser"
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userArg, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), userArg, "").
Return(nil, &model.Response{}, errors.New("Error while getting user")).
Times(1)
err := searchUserCmdF(s.client, &cobra.Command{}, []string{userArg})
s.Require().NotNil(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Equal("1 error occurred:\n\t* Error while getting user\n\n", err.Error())
s.Require().Equal("1 error occurred:\n\t* Error while getting user\n\n", printer.GetErrorLines()[0])
})
}
func (s *MmctlUnitTestSuite) TestChangePasswordUserCmdF() {
s.Run("Change password for oneself", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "userId", Username: "ExampleUser", Email: emailArg}
currentPassword := "current-password"
password := "password"
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserPassword(context.TODO(), mockUser.Id, currentPassword, password).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
cmd := &cobra.Command{}
cmd.Flags().String("password", password, "")
cmd.Flags().String("current", currentPassword, "")
err := changePasswordUserCmdF(s.client, cmd, []string{emailArg})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&mockUser, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Change password for another user", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "userId", Username: "ExampleUser", Email: emailArg}
password := "password"
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserPassword(context.TODO(), mockUser.Id, "", password).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
cmd := &cobra.Command{}
cmd.Flags().String("password", password, "")
err := changePasswordUserCmdF(s.client, cmd, []string{emailArg})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&mockUser, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Error when changing password for oneself", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "userId", Username: "ExampleUser", Email: emailArg}
mockError := errors.New("mock error")
currentPassword := "current-password"
password := "password"
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserPassword(context.TODO(), mockUser.Id, currentPassword, password).
Return(&model.Response{StatusCode: http.StatusOK}, mockError).
Times(1)
cmd := &cobra.Command{}
cmd.Flags().String("password", password, "")
cmd.Flags().String("current", currentPassword, "")
err := changePasswordUserCmdF(s.client, cmd, []string{emailArg})
s.Require().Error(err)
s.Require().EqualError(err, "changing user password failed: mock error")
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Error when changing password for another user", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "userId", Username: "ExampleUser", Email: emailArg}
mockError := errors.New("mock error")
password := "password"
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserPassword(context.TODO(), mockUser.Id, "", password).
Return(&model.Response{StatusCode: http.StatusOK}, mockError).
Times(1)
cmd := &cobra.Command{}
cmd.Flags().String("password", password, "")
err := changePasswordUserCmdF(s.client, cmd, []string{emailArg})
s.Require().Error(err)
s.Require().EqualError(err, "changing user password failed: mock error")
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Error changing password for a nonexisting user", func() {
printer.Clean()
arg := "[email protected]"
password := "password"
s.client.
EXPECT().
GetUserByEmail(context.TODO(), arg, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), arg, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), arg, "").
Return(nil, &model.Response{}, nil).
Times(1)
cmd := &cobra.Command{}
cmd.Flags().String("password", password, "")
err := changePasswordUserCmdF(s.client, cmd, []string{arg})
s.Require().Error(err)
s.Require().EqualError(err, "user [email protected] not found")
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Change password by a hashed one", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "userId", Username: "ExampleUser", Email: emailArg}
hashedPassword := "hashed-password"
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserHashedPassword(context.TODO(), mockUser.Id, hashedPassword).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
cmd := &cobra.Command{}
cmd.Flags().String("password", hashedPassword, "")
cmd.Flags().Bool("hashed", true, "")
err := changePasswordUserCmdF(s.client, cmd, []string{emailArg})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&mockUser, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Error when changing password by a hashed one", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "userId", Username: "ExampleUser", Email: emailArg}
mockError := errors.New("mock error")
hashedPassword := "hashed-password"
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserHashedPassword(context.TODO(), mockUser.Id, hashedPassword).
Return(&model.Response{StatusCode: http.StatusOK}, mockError).
Times(1)
cmd := &cobra.Command{}
cmd.Flags().String("password", hashedPassword, "")
cmd.Flags().Bool("hashed", true, "")
err := changePasswordUserCmdF(s.client, cmd, []string{emailArg})
s.Require().EqualError(err, "changing user hashed password failed: mock error")
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
}
func (s *MmctlUnitTestSuite) TestSendPasswordResetEmailCmd() {
s.Run("Send one reset email", func() {
printer.Clean()
emailArg := "[email protected]"
s.client.
EXPECT().
SendPasswordResetEmail(context.TODO(), emailArg).
Return(&model.Response{StatusCode: http.StatusBadRequest}, nil).
Times(1)
err := sendPasswordResetEmailCmdF(s.client, &cobra.Command{}, []string{emailArg})
s.Require().NoError(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Send one reset email and receive error on email validation", func() {
printer.Clean()
emailArg := "[email protected]"
var expected error
expected = multierror.Append(expected, fmt.Errorf("invalid email '%s'", emailArg))
err := sendPasswordResetEmailCmdF(s.client, &cobra.Command{}, []string{emailArg})
s.Require().EqualError(err, expected.Error())
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 1)
s.Require().Equal("Invalid email '"+emailArg+"'", printer.GetErrorLines()[0])
})
s.Run("Send one reset email and receive error on email sending", func() {
printer.Clean()
emailArg := "[email protected]"
mockError := errors.New("mock error")
s.client.
EXPECT().
SendPasswordResetEmail(context.TODO(), emailArg).
Return(&model.Response{StatusCode: http.StatusBadRequest}, mockError).
Times(1)
var expected error
expected = multierror.Append(expected, fmt.Errorf("unable send reset password email to email %s: %w", emailArg, mockError))
err := sendPasswordResetEmailCmdF(s.client, &cobra.Command{}, []string{emailArg})
s.Require().EqualError(err, expected.Error())
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 1)
s.Require().Equal("Unable send reset password email to email "+emailArg+". Error: "+mockError.Error(), printer.GetErrorLines()[0])
})
s.Run("Send several reset emails and receive some errors", func() {
printer.Clean()
emailArg := []string{
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]"}
mockError := errors.New("mock error")
var expected error
for _, email := range emailArg {
switch {
case strings.HasPrefix(email, "error"):
s.client.
EXPECT().
SendPasswordResetEmail(context.TODO(), email).
Return(&model.Response{StatusCode: http.StatusBadRequest}, mockError).
Times(1)
expected = multierror.Append(expected, fmt.Errorf("unable send reset password email to email %s: %w", email, mockError))
case strings.ToLower(email) != email:
expected = multierror.Append(expected, fmt.Errorf("invalid email '%s'", email))
default:
s.client.
EXPECT().
SendPasswordResetEmail(context.TODO(), email).
Return(&model.Response{StatusCode: http.StatusBadRequest}, nil).
Times(1)
}
}
err := sendPasswordResetEmailCmdF(s.client, &cobra.Command{}, emailArg)
s.Require().EqualError(err, expected.Error())
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 2)
s.Require().Equal("Unable send reset password email to email "+emailArg[1]+". Error: "+mockError.Error(), printer.GetErrorLines()[0])
s.Require().Equal("Invalid email '"+emailArg[2]+"'", printer.GetErrorLines()[1])
})
}
func (s *MmctlUnitTestSuite) TestUserInviteCmd() {
s.Run("Invite user to an existing team by Id", func() {
printer.Clean()
argUser := "[email protected]"
argTeam := "teamId"
s.client.
EXPECT().
GetTeam(context.TODO(), argTeam, "").
Return(&model.Team{Id: argTeam}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
InviteUsersToTeam(context.TODO(), argTeam, []string{argUser}).
Return(&model.Response{StatusCode: http.StatusBadRequest}, nil).
Times(1)
err := userInviteCmdF(s.client, &cobra.Command{}, []string{argUser, argTeam})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal("Invites may or may not have been sent.", printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Invite user to an existing team by name", func() {
printer.Clean()
argUser := "[email protected]"
argTeam := "teamName"
resultID := "teamId"
s.client.
EXPECT().
GetTeam(context.TODO(), argTeam, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetTeamByName(context.TODO(), argTeam, "").
Return(&model.Team{Id: resultID}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
InviteUsersToTeam(context.TODO(), resultID, []string{argUser}).
Return(&model.Response{StatusCode: http.StatusBadRequest}, nil).
Times(1)
err := userInviteCmdF(s.client, &cobra.Command{}, []string{argUser, argTeam})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal("Invites may or may not have been sent.", printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Invite user to several existing teams by name and id", func() {
printer.Clean()
argUser := "[email protected]"
argTeam := []string{"teamName1", "teamId2", "teamId3", "teamName4"}
resultTeamModels := [4]*model.Team{
{Id: "teamId1"},
{Id: "teamId2"},
{Id: "teamId3"},
{Id: "teamId4"},
}
// Setup GetTeam
s.client.
EXPECT().
GetTeam(context.TODO(), argTeam[0], "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetTeam(context.TODO(), argTeam[1], "").
Return(resultTeamModels[1], &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetTeam(context.TODO(), argTeam[2], "").
Return(resultTeamModels[2], &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetTeam(context.TODO(), argTeam[3], "").
Return(nil, &model.Response{}, nil).
Times(1)
// Setup GetTeamByName
s.client.
EXPECT().
GetTeamByName(context.TODO(), argTeam[0], "").
Return(resultTeamModels[0], &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetTeamByName(context.TODO(), argTeam[3], "").
Return(resultTeamModels[3], &model.Response{}, nil).
Times(1)
// Setup InviteUsersToTeam
for _, resultTeamModel := range resultTeamModels {
s.client.
EXPECT().
InviteUsersToTeam(context.TODO(), resultTeamModel.Id, []string{argUser}).
Return(&model.Response{StatusCode: http.StatusBadRequest}, nil).
Times(1)
}
err := userInviteCmdF(s.client, &cobra.Command{}, append([]string{argUser}, argTeam...))
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), len(argTeam))
for i := range argTeam {
s.Require().Equal("Invites may or may not have been sent.", printer.GetLines()[i])
}
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Invite user to an un-existing team", func() {
printer.Clean()
argUser := "[email protected]"
argTeam := "unexistent"
s.client.
EXPECT().
GetTeam(context.TODO(), argTeam, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetTeamByName(context.TODO(), argTeam, "").
Return(nil, &model.Response{}, nil).
Times(1)
err := userInviteCmdF(s.client, &cobra.Command{}, []string{argUser, argTeam})
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 1)
s.Require().Equal("can't find team '"+argTeam+"'", printer.GetErrorLines()[0])
})
s.Run("Invite user to an existing team and fail invite", func() {
printer.Clean()
argUser := "[email protected]"
argTeam := "teamId"
resultName := "teamName"
mockError := errors.New("mock error")
s.client.
EXPECT().
GetTeam(context.TODO(), argTeam, "").
Return(&model.Team{Id: argTeam, Name: resultName}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
InviteUsersToTeam(context.TODO(), argTeam, []string{argUser}).
Return(&model.Response{StatusCode: http.StatusBadRequest}, mockError).
Times(1)
err := userInviteCmdF(s.client, &cobra.Command{}, []string{argUser, argTeam})
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 1)
s.Require().Equal("Unable to invite user with email "+argUser+" to team "+resultName+". Error: "+mockError.Error(), printer.GetErrorLines()[0])
})
s.Run("Invite user to several existing and non-existing teams by name and id and reject one invite", func() {
printer.Clean()
argUser := "[email protected]"
argTeam := []string{"teamName1", "unexistent", "teamId3", "teamName4", "reject", "teamId6"}
resultTeamModels := [6]*model.Team{
{Id: "teamId1", Name: "teamName1"},
nil,
{Id: "teamId3", Name: "teamName3"},
{Id: "teamId4", Name: "teamName4"},
{Id: "reject", Name: "rejectName"},
{Id: "teamId6", Name: "teamName6"},
}
mockError := model.NewAppError("", "mock error", nil, "", 0)
// Setup GetTeam
s.client.
EXPECT().
GetTeam(context.TODO(), argTeam[0], "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetTeam(context.TODO(), argTeam[1], "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetTeam(context.TODO(), argTeam[2], "").
Return(resultTeamModels[2], &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetTeam(context.TODO(), argTeam[3], "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetTeam(context.TODO(), argTeam[4], "").
Return(resultTeamModels[4], &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetTeam(context.TODO(), argTeam[5], "").
Return(resultTeamModels[5], &model.Response{}, nil).
Times(1)
// Setup GetTeamByName
s.client.
EXPECT().
GetTeamByName(context.TODO(), argTeam[0], "").
Return(resultTeamModels[0], &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetTeamByName(context.TODO(), argTeam[1], "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetTeamByName(context.TODO(), argTeam[3], "").
Return(resultTeamModels[3], &model.Response{}, nil).
Times(1)
// Setup InviteUsersToTeam
s.client.
EXPECT().
InviteUsersToTeam(context.TODO(), resultTeamModels[0].Id, []string{argUser}).
Return(&model.Response{StatusCode: http.StatusBadRequest}, nil).
Times(1)
s.client.
EXPECT().
InviteUsersToTeam(context.TODO(), resultTeamModels[2].Id, []string{argUser}).
Return(&model.Response{StatusCode: http.StatusBadRequest}, nil).
Times(1)
s.client.
EXPECT().
InviteUsersToTeam(context.TODO(), resultTeamModels[3].Id, []string{argUser}).
Return(&model.Response{StatusCode: http.StatusBadRequest}, nil).
Times(1)
s.client.
EXPECT().
InviteUsersToTeam(context.TODO(), resultTeamModels[4].Id, []string{argUser}).
Return(&model.Response{StatusCode: http.StatusBadRequest}, mockError).
Times(1)
s.client.
EXPECT().
InviteUsersToTeam(context.TODO(), resultTeamModels[5].Id, []string{argUser}).
Return(&model.Response{StatusCode: http.StatusBadRequest}, nil).
Times(1)
err := userInviteCmdF(s.client, &cobra.Command{}, append([]string{argUser}, argTeam...))
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 4)
for i := range 4 {
s.Require().Equal("Invites may or may not have been sent.", printer.GetLines()[i])
}
s.Require().Len(printer.GetErrorLines(), 2)
s.Require().Equal("can't find team '"+argTeam[1]+"'", printer.GetErrorLines()[0])
s.Require().Equal("Unable to invite user with email "+argUser+" to team "+resultTeamModels[4].Name+". Error: "+mockError.Error(), printer.GetErrorLines()[1])
})
}
func (s *MmctlUnitTestSuite) TestUserCreateCmd() {
mockUser := model.User{
Username: "username",
Password: "password",
Email: "email",
}
s.Run("Create user with email missing", func() {
printer.Clean()
command := cobra.Command{}
command.Flags().String("username", mockUser.Username, "")
command.Flags().String("password", mockUser.Password, "")
err := userCreateCmdF(s.client, &command, []string{})
s.Require().Equal("Email is required: flag accessed but not defined: email", err.Error())
})
s.Run("Create user with username missing", func() {
printer.Clean()
command := cobra.Command{}
command.Flags().String("email", mockUser.Email, "")
command.Flags().String("password", mockUser.Password, "")
err := userCreateCmdF(s.client, &command, []string{})
s.Require().Equal("Username is required: flag accessed but not defined: username", err.Error())
})
s.Run("Create user with password missing", func() {
printer.Clean()
command := cobra.Command{}
command.Flags().String("username", mockUser.Username, "")
command.Flags().String("email", mockUser.Email, "")
err := userCreateCmdF(s.client, &command, []string{})
s.Require().Equal("Password is required: flag accessed but not defined: password", err.Error())
})
s.Run("Create a regular user", func() {
printer.Clean()
s.client.
EXPECT().
CreateUser(context.TODO(), &mockUser).
Return(&mockUser, &model.Response{}, nil).
Times(1)
command := cobra.Command{}
command.Flags().String("username", mockUser.Username, "")
command.Flags().String("email", mockUser.Email, "")
command.Flags().String("password", mockUser.Password, "")
err := userCreateCmdF(s.client, &command, []string{})
s.Require().Nil(err)
s.Require().Equal(&mockUser, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Create a regular user with welcome email disabled", func() {
printer.Clean()
oldDisableWelcomeEmail := mockUser.DisableWelcomeEmail
mockUser.DisableWelcomeEmail = true
defer func() { mockUser.DisableWelcomeEmail = oldDisableWelcomeEmail }()
s.client.
EXPECT().
CreateUser(context.TODO(), &mockUser).
Return(&mockUser, &model.Response{}, nil).
Times(1)
command := cobra.Command{}
command.Flags().String("username", mockUser.Username, "")
command.Flags().String("email", mockUser.Email, "")
command.Flags().String("password", mockUser.Password, "")
command.Flags().Bool("disable-welcome-email", mockUser.DisableWelcomeEmail, "")
err := userCreateCmdF(s.client, &command, []string{})
s.Require().Nil(err)
printerLines := printer.GetLines()[0]
printedUser := printerLines.(*model.User)
s.Require().Equal(&mockUser, printerLines)
s.Require().Equal(true, printedUser.DisableWelcomeEmail)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Create a regular user with client returning error", func() {
printer.Clean()
s.client.
EXPECT().
CreateUser(context.TODO(), &mockUser).
Return(&mockUser, &model.Response{}, errors.New("remote error")).
Times(1)
command := cobra.Command{}
command.Flags().String("username", mockUser.Username, "")
command.Flags().String("email", mockUser.Email, "")
command.Flags().String("password", mockUser.Password, "")
err := userCreateCmdF(s.client, &command, []string{})
s.Require().Equal("Unable to create user. Error: remote error", err.Error())
})
s.Run("Create a sysAdmin user", func() {
printer.Clean()
s.client.
EXPECT().
CreateUser(context.TODO(), &mockUser).
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserRoles(context.TODO(), mockUser.Id, "system_user system_admin").
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
command := cobra.Command{}
command.Flags().String("username", mockUser.Username, "")
command.Flags().String("email", mockUser.Email, "")
command.Flags().String("password", mockUser.Password, "")
command.Flags().Bool("system-admin", true, "")
err := userCreateCmdF(s.client, &command, []string{})
s.Require().Nil(err)
s.Require().Equal(&mockUser, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Create a guest user", func() {
printer.Clean()
s.client.
EXPECT().
CreateUser(context.TODO(), &mockUser).
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
DemoteUserToGuest(context.TODO(), mockUser.Id).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
command := cobra.Command{}
command.Flags().String("username", mockUser.Username, "")
command.Flags().String("email", mockUser.Email, "")
command.Flags().String("password", mockUser.Password, "")
command.Flags().Bool("guest", true, "")
err := userCreateCmdF(s.client, &command, []string{})
s.Require().Nil(err)
s.Require().Equal(&mockUser, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Create a sysAdmin user with client returning error", func() {
printer.Clean()
s.client.
EXPECT().
CreateUser(context.TODO(), &mockUser).
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserRoles(context.TODO(), mockUser.Id, "system_user system_admin").
Return(&model.Response{StatusCode: http.StatusBadRequest}, errors.New("remote error")).
Times(1)
command := cobra.Command{}
command.Flags().String("username", mockUser.Username, "")
command.Flags().String("email", mockUser.Email, "")
command.Flags().String("password", mockUser.Password, "")
command.Flags().Bool("system-admin", true, "")
err := userCreateCmdF(s.client, &command, []string{})
s.Require().Equal("Unable to update user roles. Error: remote error", err.Error())
})
}
func (s *MmctlUnitTestSuite) TestResetUserMfaCmd() {
s.Run("One user without problems", func() {
printer.Clean()
s.client.
EXPECT().
GetUserByUsername(context.TODO(), "userId", "").
Return(&model.User{Id: "userId"}, nil, nil).
Times(1)
s.client.
EXPECT().
UpdateUserMfa(context.TODO(), "userId", "", false).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := resetUserMfaCmdF(s.client, &cobra.Command{}, []string{"userId"})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Cannot find one user", func() {
printer.Clean()
s.client.
EXPECT().
GetUserByUsername(context.TODO(), "userId", "").
Return(nil, nil, nil).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), "userId", "").
Return(nil, nil, nil).
Times(1)
err := resetUserMfaCmdF(s.client, &cobra.Command{}, []string{"userId"})
var expected error
expected = multierror.Append(
expected, ExtractErrorFromResponse(
&model.Response{StatusCode: http.StatusNotFound},
ErrEntityNotFound{Type: "user", ID: "userId"},
),
)
s.Require().EqualError(err, expected.Error())
s.Require().Len(printer.GetLines(), 0)
})
s.Run("One user, unable to reset", func() {
printer.Clean()
mockError := errors.New("mock error")
s.client.
EXPECT().
GetUserByUsername(context.TODO(), "userId", "").
Return(&model.User{Id: "userId"}, nil, nil).
Times(1)
s.client.
EXPECT().
UpdateUserMfa(context.TODO(), "userId", "", false).
Return(&model.Response{StatusCode: http.StatusBadRequest}, mockError).
Times(1)
err := resetUserMfaCmdF(s.client, &cobra.Command{}, []string{"userId"})
var expected error
expected = multierror.Append(
expected, fmt.Errorf("unable to reset user \"userId\" MFA. Error: %s", mockError.Error()),
)
s.Require().EqualError(err, expected.Error())
s.Require().Len(printer.GetLines(), 0)
})
s.Run("Several users, with unknown users and users unable to be reset", func() {
printer.Clean()
users := []string{"user0", "error1", "user2", "notfounduser", "user4"}
mockError := errors.New("mock error")
for _, user := range users {
if user == "notfounduser" {
s.client.
EXPECT().
GetUserByUsername(context.TODO(), user, "").
Return(nil, nil, nil).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), user, "").
Return(nil, nil, nil).
Times(1)
} else {
s.client.
EXPECT().
GetUserByUsername(context.TODO(), user, "").
Return(&model.User{Id: user}, nil, nil).
Times(1)
}
}
for _, user := range users {
if user == "error1" {
s.client.
EXPECT().
UpdateUserMfa(context.TODO(), user, "", false).
Return(&model.Response{StatusCode: http.StatusBadRequest}, mockError).
Times(1)
} else if user != "notfounduser" {
s.client.
EXPECT().
UpdateUserMfa(context.TODO(), user, "", false).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
}
}
err := resetUserMfaCmdF(s.client, &cobra.Command{}, users)
var expected *multierror.Error
expected = multierror.Append(
expected, ExtractErrorFromResponse(
&model.Response{StatusCode: http.StatusNotFound},
ErrEntityNotFound{Type: "user", ID: users[3]},
),
)
expected = multierror.Append(
expected, fmt.Errorf("unable to reset user \"%s\" MFA. Error: %s", users[1], mockError.Error()),
)
s.Require().EqualError(err, expected.ErrorOrNil().Error())
s.Require().Len(printer.GetLines(), 0)
})
}
func (s *MmctlUnitTestSuite) TestListUserCmdF() {
s.Run("Listing users with paging", func() {
printer.Clean()
email := "[email protected]"
mockUser := model.User{Username: "ExampleUser", Email: email}
cmd := ResetListUsersCmd(s.T())
s.Require().NoError(cmd.Flags().Set("per-page", "1"))
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 0, 1, "", "").
Return([]*model.User{&mockUser}, &model.Response{}, nil).
Times(1)
err := listUsersCmdF(s.client, cmd, []string{})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&mockUser, printer.GetLines()[0])
})
s.Run("Listing all the users", func() {
printer.Clean()
email1 := "[email protected]"
mockUser1 := model.User{Username: "ExampleUser1", Email: email1}
email2 := "[email protected]"
mockUser2 := model.User{Username: "ExampleUser2", Email: email2}
cmd := ResetListUsersCmd(s.T())
s.Require().NoError(cmd.Flags().Set("per-page", "1"))
s.Require().NoError(cmd.Flags().Set("all", "true"))
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 0, 1, "", "").
Return([]*model.User{&mockUser1}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 1, 1, "", "").
Return([]*model.User{&mockUser2}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 2, 1, "", "").
Return([]*model.User{}, &model.Response{}, nil).
Times(1)
err := listUsersCmdF(s.client, cmd, []string{})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 2)
s.Require().Equal(&mockUser1, printer.GetLines()[0])
s.Require().Equal(&mockUser2, printer.GetLines()[1])
})
s.Run("Listing all users including inactive", func() {
printer.Clean()
email1 := "[email protected]"
mockUser1 := model.User{Username: "ExampleUser1", Email: email1}
email2 := "[email protected]"
mockUser2 := model.User{Username: "ExampleUser2", Email: email2, DeleteAt: model.GetMillis()}
cmd := ResetListUsersCmd(s.T())
s.Require().NoError(cmd.Flags().Set("per-page", "1"))
s.Require().NoError(cmd.Flags().Set("all", "true"))
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 0, 1, "", "").
Return([]*model.User{&mockUser1}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 1, 1, "", "").
Return([]*model.User{&mockUser2}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 2, 1, "", "").
Return([]*model.User{}, &model.Response{}, nil).
Times(1)
err := listUsersCmdF(s.client, cmd, []string{})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 2)
s.Require().Equal(&mockUser1, printer.GetLines()[0])
s.Require().Equal(&mockUser2, printer.GetLines()[1])
})
s.Run("Listing all inactive users", func() {
printer.Clean()
email2 := "[email protected]"
mockUser2 := model.User{Username: "ExampleUser2", Email: email2, DeleteAt: model.GetMillis()}
email3 := "[email protected]"
mockUser3 := model.User{Username: "ExampleUser3", Email: email3, DeleteAt: model.GetMillis()}
cmd := ResetListUsersCmd(s.T())
s.Require().NoError(cmd.Flags().Set("per-page", "1"))
s.Require().NoError(cmd.Flags().Set("all", "true"))
s.Require().NoError(cmd.Flags().Set("inactive", "true"))
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 0, 1, "inactive=true", "").
Return([]*model.User{&mockUser2}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 1, 1, "inactive=true", "").
Return([]*model.User{&mockUser3}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 2, 1, "inactive=true", "").
Return([]*model.User{}, &model.Response{}, nil).
Times(1)
err := listUsersCmdF(s.client, cmd, []string{})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 2)
s.Require().Equal(&mockUser2, printer.GetLines()[0])
s.Require().Equal(&mockUser3, printer.GetLines()[1])
})
s.Run("Listing all active users", func() {
printer.Clean()
email1 := "[email protected]"
mockUser1 := model.User{Username: "ExampleUser1", Email: email1}
email2 := "[email protected]"
mockUser2 := model.User{Username: "ExampleUser2", Email: email2}
cmd := ResetListUsersCmd(s.T())
s.Require().NoError(cmd.Flags().Set("per-page", "1"))
s.Require().NoError(cmd.Flags().Set("all", "true"))
s.Require().NoError(cmd.Flags().Set("active", "true"))
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 0, 1, "active=true", "").
Return([]*model.User{&mockUser1}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 1, 1, "active=true", "").
Return([]*model.User{&mockUser2}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 2, 1, "active=true", "").
Return([]*model.User{}, &model.Response{}, nil).
Times(1)
err := listUsersCmdF(s.client, cmd, []string{})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 2)
s.Require().Equal(&mockUser1, printer.GetLines()[0])
s.Require().Equal(&mockUser2, printer.GetLines()[1])
})
s.Run("Fail to list users when both active and inactive flags are set", func() {
printer.Clean()
cmd := ResetListUsersCmd(s.T())
s.Require().NoError(cmd.Flags().Set("active", "true"))
s.Require().NoError(cmd.Flags().Set("inactive", "true"))
err := listUsersCmdF(s.client, cmd, []string{})
s.Require().Error(err)
s.Require().EqualError(err, "--active and --inactive flags cannot be used together")
s.Require().Empty(printer.GetLines())
})
s.Run("Listing inactive users with paging skipping 1 page", func() {
printer.Clean()
email := "[email protected]"
_ = model.User{Username: "ExampleUser", Email: email, DeleteAt: model.GetMillis()}
email1 := "[email protected]"
mockUser1 := model.User{Username: "ExampleUser1", Email: email1, DeleteAt: model.GetMillis()}
cmd := ResetListUsersCmd(s.T())
s.Require().NoError(cmd.Flags().Set("page", "1"))
s.Require().NoError(cmd.Flags().Set("per-page", "1"))
s.Require().NoError(cmd.Flags().Set("inactive", "true"))
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 1, 1, "inactive=true", "").
Return([]*model.User{&mockUser1}, &model.Response{}, nil).
Times(1)
err := listUsersCmdF(s.client, cmd, []string{})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&mockUser1, printer.GetLines()[0])
})
s.Run("Try to list all the users when there are no uses in store", func() {
printer.Clean()
cmd := ResetListUsersCmd(s.T())
s.Require().NoError(cmd.Flags().Set("page", "0"))
s.Require().NoError(cmd.Flags().Set("per-page", "1"))
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 0, 1, "", "").
Return([]*model.User{}, &model.Response{}, nil).
Times(1)
err := listUsersCmdF(s.client, cmd, []string{})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 0)
})
s.Run("Return an error from GetUsersWithCustomQueryParameters call and verify that error is properly returned", func() {
printer.Clean()
cmd := ResetListUsersCmd(s.T())
s.Require().NoError(cmd.Flags().Set("page", "0"))
s.Require().NoError(cmd.Flags().Set("per-page", "1"))
mockError := errors.New("mock error")
mockErrorW := errors.Wrap(mockError, "Failed to fetch users")
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 0, 1, "", "").
Return(nil, &model.Response{}, mockError).
Times(1)
err := listUsersCmdF(s.client, cmd, []string{})
s.Require().NotNil(err)
s.Require().EqualError(err, mockErrorW.Error())
})
s.Run("Start with page 2 where a server has total 3 pages", func() {
printer.Clean()
email := "[email protected]"
mockUser := model.User{Username: "ExampleUser", Email: email}
cmd := ResetListUsersCmd(s.T())
s.Require().NoError(cmd.Flags().Set("page", "2"))
s.Require().NoError(cmd.Flags().Set("per-page", "1"))
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 2, 1, "", "").
Return([]*model.User{&mockUser}, &model.Response{}, nil).
Times(1)
err := listUsersCmdF(s.client, cmd, []string{})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&mockUser, printer.GetLines()[0])
})
s.Run("Listing users for given team", func() {
printer.Clean()
email := "[email protected]"
mockUser := model.User{Username: "ExampleUser", Email: email}
resultID := "teamId"
team := "teamName"
cmd := ResetListUsersCmd(s.T())
s.Require().NoError(cmd.Flags().Set("page", "0"))
s.Require().NoError(cmd.Flags().Set("per-page", "1"))
s.Require().NoError(cmd.Flags().Set("team", team))
s.client.
EXPECT().
GetTeamByName(context.TODO(), team, "").
Return(&model.Team{Id: resultID}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 0, 1, "in_team="+resultID, "").
Return([]*model.User{&mockUser}, &model.Response{}, nil).
Times(1)
err := listUsersCmdF(s.client, cmd, []string{})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&mockUser, printer.GetLines()[0])
})
s.Run("Listing inactive users for given team", func() {
printer.Clean()
email := "[email protected]"
_ = model.User{Username: "ExampleUser", Email: email}
email1 := "[email protected]"
mockUser1 := model.User{Username: "ExampleUser1", Email: email1, DeleteAt: model.GetMillis()}
resultID := "teamId"
team := "teamName"
cmd := ResetListUsersCmd(s.T())
s.Require().NoError(cmd.Flags().Set("page", "0"))
s.Require().NoError(cmd.Flags().Set("per-page", "1"))
s.Require().NoError(cmd.Flags().Set("team", team))
s.client.
EXPECT().
GetTeamByName(context.TODO(), team, "").
Return(&model.Team{Id: resultID}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 0, 1, "in_team="+resultID, "").
Return([]*model.User{&mockUser1}, &model.Response{}, nil).
Times(1)
err := listUsersCmdF(s.client, cmd, []string{})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&mockUser1, printer.GetLines()[0])
})
s.Run("Listing all inactive users for given team", func() {
printer.Clean()
email := "[email protected]"
_ = model.User{Username: "ExampleUser", Email: email}
email1 := "[email protected]"
mockUser1 := model.User{Username: "ExampleUser1", Email: email1, DeleteAt: model.GetMillis()}
email2 := "[email protected]"
mockUser2 := model.User{Username: "ExampleUser2", Email: email2, DeleteAt: model.GetMillis()}
resultID := "teamId"
team := "teamName"
cmd := ResetListUsersCmd(s.T())
s.Require().NoError(cmd.Flags().Set("page", "0"))
s.Require().NoError(cmd.Flags().Set("per-page", "1"))
s.Require().NoError(cmd.Flags().Set("team", team))
s.Require().NoError(cmd.Flags().Set("all", "true"))
s.client.
EXPECT().
GetTeamByName(context.TODO(), team, "").
Return(&model.Team{Id: resultID}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 0, 1, "in_team="+resultID, "").
Return([]*model.User{&mockUser1}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 1, 1, "in_team="+resultID, "").
Return([]*model.User{&mockUser2}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 2, 1, "in_team="+resultID, "").
Return([]*model.User{}, &model.Response{}, nil).
Times(1)
err := listUsersCmdF(s.client, cmd, []string{})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 2)
s.Require().Equal(&mockUser1, printer.GetLines()[0])
s.Require().Equal(&mockUser2, printer.GetLines()[1])
})
s.Run("Listing users for given role", func() {
printer.Clean()
email1 := "[email protected]"
_ = model.User{Username: "ExampleUser1", Email: email1, Roles: "system_user"}
email2 := "[email protected]"
mockUser2 := model.User{Username: "ExampleUser2", Email: email2, Roles: "system_user system_admin"}
role := "system_admin"
cmd := ResetListUsersCmd(s.T())
s.Require().NoError(cmd.Flags().Set("page", "0"))
s.Require().NoError(cmd.Flags().Set("per-page", "5"))
s.Require().NoError(cmd.Flags().Set("role", role))
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 0, 5, "role="+role, "").
Return([]*model.User{&mockUser2}, &model.Response{}, nil).
Times(1)
err := listUsersCmdF(s.client, cmd, []string{})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&mockUser2, printer.GetLines()[0])
})
s.Run("Listing all users for given role", func() {
printer.Clean()
email1 := "[email protected]"
mockUser1 := model.User{Username: "ExampleUser1", Email: email1, Roles: "system_user"}
email2 := "[email protected]"
mockUser2 := model.User{Username: "ExampleUser2", Email: email2, Roles: "system_user system_admin"}
role := "system_user"
cmd := ResetListUsersCmd(s.T())
s.Require().NoError(cmd.Flags().Set("page", "0"))
s.Require().NoError(cmd.Flags().Set("per-page", "1"))
s.Require().NoError(cmd.Flags().Set("role", role))
s.Require().NoError(cmd.Flags().Set("all", "true"))
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 0, 1, "role="+role, "").
Return([]*model.User{&mockUser1}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 1, 1, "role="+role, "").
Return([]*model.User{&mockUser2}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUsersWithCustomQueryParameters(context.TODO(), 2, 1, "role="+role, "").
Return([]*model.User{}, &model.Response{}, nil).
Times(1)
err := listUsersCmdF(s.client, cmd, []string{})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 2)
s.Require().Equal(&mockUser1, printer.GetLines()[0])
s.Require().Equal(&mockUser2, printer.GetLines()[1])
})
}
func (s *MmctlUnitTestSuite) TestUserDeactivateCmd() {
s.Run("Deactivate an existing user using email", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Username: "ExampleUser", Email: emailArg}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser.Id, false).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := userDeactivateCmdF(s.client, &cobra.Command{}, []string{mockUser.Email})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Deactivate an existing user by username", func() {
printer.Clean()
emailArg := "[email protected]"
usernameArg := "ExampleUser"
mockUser := model.User{Username: usernameArg, Email: emailArg}
s.client.
EXPECT().
GetUserByUsername(context.TODO(), usernameArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser.Id, false).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := userDeactivateCmdF(s.client, &cobra.Command{}, []string{mockUser.Username})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Deactivate an existing user by id", func() {
printer.Clean()
mockUser := model.User{Id: "userId1", Username: "ExampleUser", Email: "[email protected]"}
s.client.
EXPECT().
GetUserByUsername(context.TODO(), mockUser.Id, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), mockUser.Id, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser.Id, false).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := userDeactivateCmdF(s.client, &cobra.Command{}, []string{mockUser.Id})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Deactivate SSO user", func() {
printer.Clean()
arg := "[email protected]"
mockUser := model.User{Id: "example-user", Username: "ExampleUser", Email: arg, AuthService: "SSO"}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), arg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser.Id, false).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := userDeactivateCmdF(s.client, &cobra.Command{}, []string{arg})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal("You must also deactivate user "+mockUser.Id+" in the SSO provider or they will be reactivated on next login or sync.", printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Deactivate nonexistent user", func() {
printer.Clean()
arg := "[email protected]"
s.client.
EXPECT().
GetUserByEmail(context.TODO(), arg, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), arg, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), arg, "").
Return(nil, &model.Response{}, nil).
Times(1)
err := userDeactivateCmdF(s.client, &cobra.Command{}, []string{arg})
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 1)
s.Require().Equal(fmt.Sprintf("1 error occurred:\n\t* user %s not found\n\n", arg), printer.GetErrorLines()[0])
})
s.Run("Delete multiple users", func() {
printer.Clean()
mockUser1 := model.User{Id: "userId1", Email: "[email protected]", Username: "user1"}
mockUser2 := model.User{Id: "userId2", Email: "[email protected]", Username: "user2"}
mockUser3 := model.User{Id: "userId3", Email: "[email protected]", Username: "user3"}
argEmails := []string{mockUser1.Email, mockUser2.Email, mockUser3.Email}
argUsers := []model.User{mockUser1, mockUser2, mockUser3}
for i := range argEmails {
s.client.
EXPECT().
GetUserByEmail(context.TODO(), argEmails[i], "").
Return(&argUsers[i], &model.Response{}, nil).
Times(1)
}
for i := range argEmails {
s.client.
EXPECT().
UpdateUserActive(context.TODO(), argUsers[i].Id, false).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
}
err := userDeactivateCmdF(s.client, &cobra.Command{}, argEmails)
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Delete multiple users with argument mixture of emails usernames and userIds", func() {
printer.Clean()
mockUser1 := model.User{Id: "userId1", Email: "[email protected]", Username: "user1"}
mockUser2 := model.User{Id: "userId2", Email: "[email protected]", Username: "user2"}
mockUser3 := model.User{Id: "userId3", Email: "[email protected]", Username: "user3"}
argsDelete := []string{mockUser1.Id, mockUser2.Email, mockUser3.Username}
argUsers := []model.User{mockUser1, mockUser2, mockUser3}
// mockUser1
s.client.
EXPECT().
GetUserByUsername(context.TODO(), argsDelete[0], "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), argsDelete[0], "").
Return(&argUsers[0], &model.Response{}, nil).
Times(1)
// mockUser2
s.client.
EXPECT().
GetUserByEmail(context.TODO(), argsDelete[1], "").
Return(&argUsers[1], &model.Response{}, nil).
Times(1)
// mockUser3
s.client.
EXPECT().
GetUserByUsername(context.TODO(), argsDelete[2], "").
Return(&argUsers[2], &model.Response{}, nil).
Times(1)
for _, user := range argUsers {
s.client.
EXPECT().
UpdateUserActive(context.TODO(), user.Id, false).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
}
err := userDeactivateCmdF(s.client, &cobra.Command{}, argsDelete)
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Delete multiple users with an non existent user", func() {
printer.Clean()
mockUser1 := model.User{Id: "userId1", Email: "[email protected]", Username: "user1"}
nonexistentEmail := "[email protected]"
// mockUser1
s.client.
EXPECT().
GetUserByEmail(context.TODO(), mockUser1.Email, "").
Return(&mockUser1, &model.Response{}, nil).
Times(1)
// nonexistent email
s.client.
EXPECT().
GetUserByEmail(context.TODO(), nonexistentEmail, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), nonexistentEmail, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), nonexistentEmail, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserActive(context.TODO(), mockUser1.Id, false).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := userDeactivateCmdF(s.client, &cobra.Command{}, []string{mockUser1.Email, nonexistentEmail})
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 1)
s.Require().Equal(fmt.Sprintf("1 error occurred:\n\t* user %s not found\n\n", nonexistentEmail), printer.GetErrorLines()[0])
})
}
func (s *MmctlUnitTestSuite) TestVerifyUserEmailWithoutTokenCmd() {
s.Run("Verify user", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "example", Email: emailArg}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
VerifyUserEmailWithoutToken(context.TODO(), mockUser.Id).
Return(&mockUser, &model.Response{}, nil).
Times(1)
err := verifyUserEmailWithoutTokenCmdF(s.client, &cobra.Command{}, []string{emailArg})
s.Require().NoError(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&mockUser, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Couldn't find the user", func() {
printer.Clean()
userArg := "bad-user-id"
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userArg, "").
Return(nil, &model.Response{StatusCode: http.StatusNotFound}, errors.New("")).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), userArg, "").
Return(nil, &model.Response{StatusCode: http.StatusNotFound}, errors.New("")).
Times(1)
err := verifyUserEmailWithoutTokenCmdF(s.client, &cobra.Command{}, []string{userArg})
var expected error
expected = multierror.Append(
expected, ExtractErrorFromResponse(
&model.Response{StatusCode: http.StatusNotFound},
ErrEntityNotFound{Type: "user", ID: userArg},
),
)
s.Require().EqualError(err, expected.Error())
s.Require().Len(printer.GetLines(), 0)
})
s.Run("Could not verify user", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "example", Email: emailArg}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
VerifyUserEmailWithoutToken(context.TODO(), mockUser.Id).
Return(nil, &model.Response{}, errors.New("some-message")).
Times(1)
err := verifyUserEmailWithoutTokenCmdF(s.client, &cobra.Command{}, []string{emailArg})
var expected error
expected = multierror.Append(
expected, fmt.Errorf("unable to verify user %s email: %s", mockUser.Id, errors.New("some-message")),
)
s.Require().EqualError(err, expected.Error())
s.Require().Len(printer.GetLines(), 0)
})
}
func (s *MmctlUnitTestSuite) TestUserConvertCmd() {
s.Run("convert user to a bot", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "example", Email: emailArg}
mockBot := model.Bot{UserId: "example"}
cmd := &cobra.Command{}
cmd.Flags().Bool("bot", true, "")
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
ConvertUserToBot(context.TODO(), mockUser.Id).
Return(&mockBot, &model.Response{}, nil).
Times(1)
err := userConvertCmdF(s.client, cmd, []string{emailArg})
s.Require().NoError(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&mockBot, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("convert bot to a user", func() {
printer.Clean()
userNameArg := "example-bot"
mockUser := model.User{Id: "example", Email: "[email protected]"}
mockBot := model.Bot{UserId: "example"}
mockBotUser := model.User{Id: "example", Username: userNameArg, IsBot: true}
userPatch := model.UserPatch{
Email: new("[email protected]"),
Password: new("password"),
Username: new("example-user"),
}
cmd := &cobra.Command{}
cmd.Flags().Bool("user", true, "")
cmd.Flags().String("password", "password", "")
cmd.Flags().String("email", "[email protected]", "")
cmd.Flags().String("username", "example-user", "")
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userNameArg, "").
Return(&mockBotUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
ConvertBotToUser(context.TODO(), mockBot.UserId, &userPatch, false).
Return(&mockUser, &model.Response{}, nil).
Times(1)
err := userConvertCmdF(s.client, cmd, []string{userNameArg})
s.Require().NoError(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&mockUser, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("fail for not providing either user or bot flag", func() {
printer.Clean()
emailArg := "[email protected]"
cmd := &cobra.Command{}
err := userConvertCmdF(s.client, cmd, []string{emailArg})
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 0)
})
s.Run("got error while converting a user to a bot", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "example", Email: emailArg}
cmd := &cobra.Command{}
cmd.Flags().Bool("bot", true, "")
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
ConvertUserToBot(context.TODO(), mockUser.Id).
Return(nil, &model.Response{}, errors.New("some-message")).
Times(1)
err := userConvertCmdF(s.client, cmd, []string{emailArg})
s.Require().Error(err)
s.Require().Equal("1 error occurred:\n\t* some-message\n\n", err.Error())
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("got error while converting a bot to a user", func() {
printer.Clean()
userNameArg := "example-bot"
mockBot := model.Bot{UserId: "example"}
mockBotUser := model.User{Id: "example", Username: userNameArg, IsBot: true}
userPatch := model.UserPatch{
Email: new("[email protected]"),
Password: new("password"),
Username: new("example-user"),
}
cmd := &cobra.Command{}
cmd.Flags().Bool("user", true, "")
cmd.Flags().String("password", "password", "")
cmd.Flags().String("email", "[email protected]", "")
cmd.Flags().String("username", "example-user", "")
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userNameArg, "").
Return(&mockBotUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
ConvertBotToUser(context.TODO(), mockBot.UserId, &userPatch, false).
Return(nil, &model.Response{}, errors.New("some-message")).
Times(1)
err := userConvertCmdF(s.client, cmd, []string{userNameArg})
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 0)
})
}
func (s *MmctlUnitTestSuite) TestMigrateAuthCmd() {
s.Run("Successfully convert auth to LDAP", func() {
printer.Clean()
fromAuth := "email"
toAuth := "ldap"
matchField := "username"
cmd := &cobra.Command{}
cmd.Flags().Bool("force", false, "")
s.client.
EXPECT().
MigrateAuthToLdap(context.TODO(), fromAuth, matchField, false).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := migrateAuthCmdF(s.client, cmd, []string{fromAuth, toAuth, matchField})
s.Require().NoError(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Successfully convert auth to SAML", func() {
printer.Clean()
fromAuth := "email"
toAuth := "saml"
file, err := os.CreateTemp("", "users.json")
s.Require().NoError(err)
defer os.Remove(file.Name())
usersFile := file.Name()
userData := map[string]string{
"[email protected]": "usr.one",
"[email protected]": "usr.two",
}
b, err := json.MarshalIndent(userData, "", " ")
s.Require().NoError(err)
_, err = file.Write(b)
s.Require().NoError(err)
err = file.Sync()
s.Require().NoError(err)
cmd := &cobra.Command{}
cmd.Flags().Bool("auto", false, "")
s.client.
EXPECT().
MigrateAuthToSaml(context.TODO(), fromAuth, userData, false).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err = migrateAuthCmdF(s.client, cmd, []string{fromAuth, toAuth, usersFile})
s.Require().NoError(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Successfully convert auth to SAML (auto)", func() {
printer.Clean()
fromAuth := "email"
toAuth := "saml"
cmd := &cobra.Command{}
cmd.Flags().Bool("auto", true, "")
cmd.Flags().Bool("confirm", true, "")
s.client.
EXPECT().
MigrateAuthToSaml(context.TODO(), fromAuth, map[string]string{}, true).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := migrateAuthCmdF(s.client, cmd, []string{fromAuth, toAuth})
s.Require().NoError(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Invalid from auth type", func() {
printer.Clean()
fromAuth := "onelogin"
toAuth := "ldap"
matchField := "username"
cmd := &cobra.Command{}
cmd.Flags().Bool("auto", true, "")
cmd.Flags().Bool("confirm", true, "")
err := migrateAuthCmdF(s.client, cmd, []string{fromAuth, toAuth, matchField})
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 0)
})
s.Run("Invalid matchfiled type for migrating auth to LDAP", func() {
printer.Clean()
fromAuth := "email"
toAuth := "ldap"
matchField := "groups"
cmd := &cobra.Command{}
cmd.Flags().Bool("force", false, "")
err := migrateAuthCmdF(s.client, cmd, []string{fromAuth, toAuth, matchField})
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 0)
})
s.Run("Fail on convert auth to SAML due to invalid file", func() {
printer.Clean()
fromAuth := "email"
toAuth := "saml"
usersFile := "./nofile.json"
cmd := &cobra.Command{}
cmd.Flags().Bool("auto", false, "")
err := migrateAuthCmdF(s.client, cmd, []string{fromAuth, toAuth, usersFile})
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 0)
})
s.Run("Failed to convert auth to LDAP from server", func() {
printer.Clean()
fromAuth := "email"
toAuth := "ldap"
matchField := "username"
cmd := &cobra.Command{}
cmd.Flags().Bool("force", false, "")
s.client.
EXPECT().
MigrateAuthToLdap(context.TODO(), fromAuth, matchField, false).
Return(&model.Response{StatusCode: http.StatusBadRequest}, errors.New("some-error")).
Times(1)
err := migrateAuthCmdF(s.client, cmd, []string{fromAuth, toAuth, matchField})
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 0)
})
s.Run("Failed to convert auth to SAML (auto) from server", func() {
printer.Clean()
fromAuth := "email"
toAuth := "saml"
cmd := &cobra.Command{}
cmd.Flags().Bool("auto", true, "")
cmd.Flags().Bool("confirm", true, "")
s.client.
EXPECT().
MigrateAuthToSaml(context.TODO(), fromAuth, map[string]string{}, true).
Return(&model.Response{StatusCode: http.StatusBadRequest}, errors.New("some-error")).
Times(1)
err := migrateAuthCmdF(s.client, cmd, []string{fromAuth, toAuth})
s.Require().Error(err)
s.Require().Len(printer.GetLines(), 0)
})
}
func (s *MmctlUnitTestSuite) TestPromoteGuestToUserCmd() {
s.Run("promote a guest to a user", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "example", Email: emailArg}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
PromoteGuestToUser(context.TODO(), mockUser.Id).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := promoteGuestToUserCmdF(s.client, nil, []string{emailArg})
s.Require().NoError(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&mockUser, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("cannot promote a guest to a user", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "example", Email: emailArg}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
PromoteGuestToUser(context.TODO(), mockUser.Id).
Return(&model.Response{StatusCode: http.StatusBadRequest}, errors.New("some-error")).
Times(1)
err := promoteGuestToUserCmdF(s.client, nil, []string{emailArg})
s.Require().ErrorContains(err, "unable to promote guest")
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 1)
s.Require().Equal(fmt.Sprintf("unable to promote guest %s: %s", emailArg, "some-error"), printer.GetErrorLines()[0])
})
}
func (s *MmctlUnitTestSuite) TestDemoteUserToGuestCmd() {
s.Run("demote a user to a guest", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "example", Email: emailArg}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
DemoteUserToGuest(context.TODO(), mockUser.Id).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := demoteUserToGuestCmdF(s.client, nil, []string{emailArg})
s.Require().NoError(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&mockUser, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("cannot demote a user to a guest", func() {
printer.Clean()
emailArg := "[email protected]"
mockUser := model.User{Id: "example", Email: emailArg}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), emailArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
DemoteUserToGuest(context.TODO(), mockUser.Id).
Return(&model.Response{StatusCode: http.StatusBadRequest}, errors.New("some-error")).
Times(1)
err := demoteUserToGuestCmdF(s.client, nil, []string{emailArg})
s.Require().ErrorContains(err, "unable to demote user")
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 1)
s.Require().Equal(fmt.Sprintf("unable to demote user %s: %s", emailArg, "some-error"), printer.GetErrorLines()[0])
})
}
func (s *MmctlUnitTestSuite) TestUserEditUsernameCmd() {
s.Run("User not found", func() {
printer.Clean()
command := cobra.Command{}
userArg := "testUser"
newUsername := "newusername"
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userArg, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), userArg, "").
Return(nil, &model.Response{}, nil).
Times(1)
err := userEditUsernameCmdF(s.client, &command, []string{userArg, newUsername})
s.Require().EqualError(err, "user testUser not found")
})
s.Run("Edit username successfully", func() {
printer.Clean()
command := cobra.Command{}
userArg := "testUser"
newUsername := "newusername"
mockUser := model.User{Id: "userId", Username: "testUser", Email: "[email protected]"}
updatedUser := model.User{Id: "userId", Username: "newusername", Email: "[email protected]"}
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUser(context.TODO(), &updatedUser).
Return(&updatedUser, &model.Response{}, nil).
Times(1)
err := userEditUsernameCmdF(s.client, &command, []string{userArg, newUsername})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&updatedUser, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Invalid username", func() {
printer.Clean()
command := cobra.Command{}
userArg := "testUser"
newUsername := "!invalid!"
mockUser := model.User{Id: "userId", Username: "testUser", Email: "[email protected]"}
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
err := userEditUsernameCmdF(s.client, &command, []string{userArg, newUsername})
s.Require().EqualError(err, "invalid username: '!invalid!'")
})
s.Run("UpdateUser API error", func() {
printer.Clean()
command := cobra.Command{}
userArg := "testUser"
newUsername := "newusername"
mockUser := model.User{Id: "userId", Username: "testUser", Email: "[email protected]"}
updatedUser := model.User{Id: "userId", Username: "newusername", Email: "[email protected]"}
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUser(context.TODO(), &updatedUser).
Return(nil, &model.Response{}, errors.New("API error")).
Times(1)
err := userEditUsernameCmdF(s.client, &command, []string{userArg, newUsername})
s.Require().EqualError(err, "failed to update user username: API error")
})
}
func (s *MmctlUnitTestSuite) TestUserEditEmailCmd() {
s.Run("User not found", func() {
printer.Clean()
command := cobra.Command{}
userArg := "testUser"
newEmail := "[email protected]"
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userArg, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), userArg, "").
Return(nil, &model.Response{}, nil).
Times(1)
err := userEditEmailCmdF(s.client, &command, []string{userArg, newEmail})
s.Require().EqualError(err, "user testUser not found")
})
s.Run("Edit email successfully", func() {
printer.Clean()
command := cobra.Command{}
userArg := "testUser"
newEmail := "[email protected]"
mockUser := model.User{Id: "userId", Username: "testUser", Email: "[email protected]"}
updatedUser := model.User{Id: "userId", Username: "testUser", Email: "[email protected]"}
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUser(context.TODO(), &updatedUser).
Return(&updatedUser, &model.Response{}, nil).
Times(1)
err := userEditEmailCmdF(s.client, &command, []string{userArg, newEmail})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&updatedUser, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Invalid email", func() {
printer.Clean()
command := cobra.Command{}
userArg := "testUser"
newEmail := "invalidemail"
mockUser := model.User{Id: "userId", Username: "testUser", Email: "[email protected]"}
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
err := userEditEmailCmdF(s.client, &command, []string{userArg, newEmail})
s.Require().EqualError(err, "invalid email: 'invalidemail'")
})
s.Run("UpdateUser API error", func() {
printer.Clean()
command := cobra.Command{}
userArg := "testUser"
newEmail := "[email protected]"
mockUser := model.User{Id: "userId", Username: "testUser", Email: "[email protected]"}
updatedUser := model.User{Id: "userId", Username: "testUser", Email: "[email protected]"}
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUser(context.TODO(), &updatedUser).
Return(nil, &model.Response{}, errors.New("API error")).
Times(1)
err := userEditEmailCmdF(s.client, &command, []string{userArg, newEmail})
s.Require().EqualError(err, "failed to update user email: API error")
})
}
func (s *MmctlUnitTestSuite) TestUserEditAuthdataCmd() {
s.Run("User not found", func() {
printer.Clean()
command := cobra.Command{}
userArg := "testUser"
newAuthdata := "newauth123"
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userArg, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), userArg, "").
Return(nil, &model.Response{}, nil).
Times(1)
err := userEditAuthdataCmdF(s.client, &command, []string{userArg, newAuthdata})
s.Require().EqualError(err, "user testUser not found")
})
s.Run("Edit authdata successfully", func() {
printer.Clean()
command := cobra.Command{}
userArg := "testUser"
newAuthdata := "newauth123"
mockUser := model.User{Id: "userId", Username: "testUser", Email: "[email protected]", AuthData: nil}
userAuth := model.UserAuth{AuthData: &newAuthdata, AuthService: mockUser.AuthService}
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserAuth(context.TODO(), mockUser.Id, &userAuth).
Return(&userAuth, &model.Response{}, nil).
Times(1)
err := userEditAuthdataCmdF(s.client, &command, []string{userArg, newAuthdata})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(&mockUser, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Clear authdata returns error", func() {
printer.Clean()
command := cobra.Command{}
userArg := "testUser"
newAuthdata := ""
authData := "existingauth"
mockUser := model.User{Id: "userId", Username: "testUser", Email: "[email protected]", AuthData: &authData, AuthService: model.UserAuthServiceHanzo}
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
err := userEditAuthdataCmdF(s.client, &command, []string{userArg, newAuthdata})
s.Require().EqualError(err, "cannot clear authdata as the user is using gitlab to log in")
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Authdata too long", func() {
printer.Clean()
command := cobra.Command{}
userArg := "testUser"
newAuthdata := strings.Repeat("x", model.UserAuthDataMaxLength+1)
mockUser := model.User{Id: "userId", Username: "testUser", Email: "[email protected]"}
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
err := userEditAuthdataCmdF(s.client, &command, []string{userArg, newAuthdata})
s.Require().EqualError(err, fmt.Sprintf("authdata too long. Maximum length is %d characters", model.UserAuthDataMaxLength))
})
s.Run("UpdateUser API error", func() {
printer.Clean()
command := cobra.Command{}
userArg := "testUser"
newAuthdata := "newauth123"
mockUser := model.User{Id: "userId", Username: "testUser", Email: "[email protected]", AuthData: nil}
userAuth := model.UserAuth{AuthData: &newAuthdata, AuthService: mockUser.AuthService}
s.client.
EXPECT().
GetUserByUsername(context.TODO(), userArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserAuth(context.TODO(), mockUser.Id, &userAuth).
Return(nil, &model.Response{}, errors.New("API error")).
Times(1)
err := userEditAuthdataCmdF(s.client, &command, []string{userArg, newAuthdata})
s.Require().EqualError(err, "failed to update user authdata: API error")
})
}
func newUserStatusCmd() *cobra.Command {
cmd := &cobra.Command{}
cmd.Flags().String("user", "", "")
return cmd
}
func newUserStatusSetCmd() *cobra.Command {
cmd := newUserStatusCmd()
cmd.Flags().String("dnd-end-time", "", "")
return cmd
}
func (s *MmctlUnitTestSuite) TestUserStatusCmdWiring() {
s.Run("status command is registered under user with a set subcommand", func() {
s.Require().True(UserCmd.HasSubCommands())
s.Require().Contains(UserCmd.Commands(), UserStatusCmd)
s.Require().Contains(UserStatusCmd.Commands(), UserStatusSetCmd)
})
s.Run("the expected flags are registered", func() {
s.Require().NotNil(UserStatusCmd.Flags().Lookup("user"))
s.Require().NotNil(UserStatusSetCmd.Flags().Lookup("user"))
s.Require().NotNil(UserStatusSetCmd.Flags().Lookup("dnd-end-time"))
})
s.Run("argument contracts are enforced", func() {
s.Require().NoError(UserStatusCmd.Args(UserStatusCmd, []string{}))
s.Require().Error(UserStatusCmd.Args(UserStatusCmd, []string{"online"}))
s.Require().NoError(UserStatusSetCmd.Args(UserStatusSetCmd, []string{"online"}))
s.Require().Error(UserStatusSetCmd.Args(UserStatusSetCmd, []string{}))
})
}
func (s *MmctlUnitTestSuite) TestUserStatusGetCmd() {
s.Run("Get status of the current user when --user is omitted", func() {
printer.Clean()
mockUser := model.User{Id: "me-id", Username: "me"}
mockStatus := &model.Status{UserId: "me-id", Status: model.StatusOnline}
s.client.
EXPECT().
GetMe(context.TODO(), "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserStatus(context.TODO(), "me-id", "").
Return(mockStatus, &model.Response{}, nil).
Times(1)
err := userStatusGetCmdF(s.client, newUserStatusCmd(), []string{})
s.Require().NoError(err)
s.Require().Len(printer.GetErrorLines(), 0)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(mockStatus, printer.GetLines()[0])
})
s.Run("Get status of a specific user via --user", func() {
printer.Clean()
cmd := newUserStatusCmd()
err := cmd.Flags().Set("user", "[email protected]")
s.Require().NoError(err)
mockUser := model.User{Id: "target-id", Username: "target", Email: "[email protected]"}
mockStatus := &model.Status{UserId: "target-id", Status: model.StatusDnd}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), "[email protected]", "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserStatus(context.TODO(), "target-id", "").
Return(mockStatus, &model.Response{}, nil).
Times(1)
err = userStatusGetCmdF(s.client, cmd, []string{})
s.Require().NoError(err)
s.Require().Len(printer.GetErrorLines(), 0)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(mockStatus, printer.GetLines()[0])
})
s.Run("Require --user in local mode", func() {
printer.Clean()
prevLocal := viper.GetBool("local")
viper.Set("local", true)
defer viper.Set("local", prevLocal)
err := userStatusGetCmdF(s.client, newUserStatusCmd(), []string{})
s.Require().EqualError(err, "the --user flag is required in local mode")
s.Require().Len(printer.GetLines(), 0)
})
s.Run("Return error when status retrieval fails", func() {
printer.Clean()
mockUser := model.User{Id: "me-id", Username: "me"}
s.client.
EXPECT().
GetMe(context.TODO(), "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserStatus(context.TODO(), "me-id", "").
Return(nil, &model.Response{}, errors.New("status error")).
Times(1)
err := userStatusGetCmdF(s.client, newUserStatusCmd(), []string{})
s.Require().EqualError(err, "could not get status for user me-id: status error")
s.Require().Len(printer.GetLines(), 0)
})
s.Run("Return error when user cannot be found", func() {
printer.Clean()
cmd := newUserStatusCmd()
err := cmd.Flags().Set("user", "ghost")
s.Require().NoError(err)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), "ghost", "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), "ghost", "").
Return(nil, &model.Response{}, nil).
Times(1)
err = userStatusGetCmdF(s.client, cmd, []string{})
s.Require().EqualError(err, "user ghost not found")
s.Require().Len(printer.GetLines(), 0)
})
}
func (s *MmctlUnitTestSuite) TestUserStatusSetCmd() {
s.Run("Set status of the current user when --user is omitted", func() {
printer.Clean()
mockUser := model.User{Id: "me-id", Username: "me"}
wantStatus := &model.Status{UserId: "me-id", Status: model.StatusAway}
returnedStatus := &model.Status{UserId: "me-id", Status: model.StatusAway, Manual: true}
s.client.
EXPECT().
GetMe(context.TODO(), "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserStatus(context.TODO(), "me-id", wantStatus).
Return(returnedStatus, &model.Response{}, nil).
Times(1)
err := userStatusSetCmdF(s.client, newUserStatusSetCmd(), []string{model.StatusAway})
s.Require().NoError(err)
s.Require().Len(printer.GetErrorLines(), 0)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(returnedStatus, printer.GetLines()[0])
})
s.Run("Set status of a specific user via --user", func() {
printer.Clean()
cmd := newUserStatusSetCmd()
err := cmd.Flags().Set("user", "[email protected]")
s.Require().NoError(err)
mockUser := model.User{Id: "target-id", Username: "target", Email: "[email protected]"}
wantStatus := &model.Status{UserId: "target-id", Status: model.StatusOffline}
s.client.
EXPECT().
GetUserByEmail(context.TODO(), "[email protected]", "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserStatus(context.TODO(), "target-id", wantStatus).
Return(wantStatus, &model.Response{}, nil).
Times(1)
err = userStatusSetCmdF(s.client, cmd, []string{model.StatusOffline})
s.Require().NoError(err)
s.Require().Len(printer.GetErrorLines(), 0)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(wantStatus, printer.GetLines()[0])
})
s.Run("Set dnd status with an end time", func() {
printer.Clean()
endTimeArg := time.Now().Add(24 * time.Hour).Format(ISO8601Layout)
endTime, err := time.Parse(ISO8601Layout, endTimeArg)
s.Require().NoError(err)
cmd := newUserStatusSetCmd()
err = cmd.Flags().Set("dnd-end-time", endTimeArg)
s.Require().NoError(err)
mockUser := model.User{Id: "me-id", Username: "me"}
wantStatus := &model.Status{UserId: "me-id", Status: model.StatusDnd, DNDEndTime: endTime.Unix()}
s.client.
EXPECT().
GetMe(context.TODO(), "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserStatus(context.TODO(), "me-id", wantStatus).
Return(wantStatus, &model.Response{}, nil).
Times(1)
err = userStatusSetCmdF(s.client, cmd, []string{model.StatusDnd})
s.Require().NoError(err)
s.Require().Len(printer.GetErrorLines(), 0)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(wantStatus, printer.GetLines()[0])
})
s.Run("Reject an invalid status value", func() {
printer.Clean()
err := userStatusSetCmdF(s.client, newUserStatusSetCmd(), []string{"busy"})
s.Require().EqualError(err, "invalid status \"busy\", must be one of: online, away, dnd, offline")
s.Require().Len(printer.GetLines(), 0)
})
s.Run("Reject --dnd-end-time with a non-dnd status", func() {
printer.Clean()
cmd := newUserStatusSetCmd()
err := cmd.Flags().Set("dnd-end-time", time.Now().Add(time.Hour).Format(ISO8601Layout))
s.Require().NoError(err)
err = userStatusSetCmdF(s.client, cmd, []string{model.StatusAway})
s.Require().EqualError(err, "the --dnd-end-time flag can only be used with the \"dnd\" status")
s.Require().Len(printer.GetLines(), 0)
})
s.Run("Set dnd status with a UTC end time", func() {
printer.Clean()
endTime := time.Now().Add(24 * time.Hour).UTC().Truncate(time.Second)
endTimeArg := endTime.Format(time.RFC3339)
cmd := newUserStatusSetCmd()
err := cmd.Flags().Set("dnd-end-time", endTimeArg)
s.Require().NoError(err)
mockUser := model.User{Id: "me-id", Username: "me"}
wantStatus := &model.Status{UserId: "me-id", Status: model.StatusDnd, DNDEndTime: endTime.Unix()}
s.client.
EXPECT().
GetMe(context.TODO(), "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserStatus(context.TODO(), "me-id", wantStatus).
Return(wantStatus, &model.Response{}, nil).
Times(1)
err = userStatusSetCmdF(s.client, cmd, []string{model.StatusDnd})
s.Require().NoError(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(wantStatus, printer.GetLines()[0])
})
s.Run("Reject a dnd-end-time in the past", func() {
printer.Clean()
cmd := newUserStatusSetCmd()
err := cmd.Flags().Set("dnd-end-time", time.Now().Add(-time.Hour).Format(ISO8601Layout))
s.Require().NoError(err)
err = userStatusSetCmdF(s.client, cmd, []string{model.StatusDnd})
s.Require().EqualError(err, "dnd-end-time must be in the future")
s.Require().Len(printer.GetLines(), 0)
})
s.Run("Reject a malformed dnd-end-time", func() {
printer.Clean()
cmd := newUserStatusSetCmd()
err := cmd.Flags().Set("dnd-end-time", "not-a-time")
s.Require().NoError(err)
err = userStatusSetCmdF(s.client, cmd, []string{model.StatusDnd})
s.Require().EqualError(err, "invalid dnd-end-time \"not-a-time\", expected RFC3339 format (e.g. 2006-01-02T15:04:05-07:00 or 2006-01-02T15:04:05Z)")
s.Require().Len(printer.GetLines(), 0)
})
s.Run("Return error when the current user cannot be resolved", func() {
printer.Clean()
s.client.
EXPECT().
GetMe(context.TODO(), "").
Return(nil, &model.Response{}, errors.New("me error")).
Times(1)
err := userStatusSetCmdF(s.client, newUserStatusSetCmd(), []string{model.StatusOnline})
s.Require().EqualError(err, "could not retrieve the current user: me error")
s.Require().Len(printer.GetLines(), 0)
})
s.Run("Require --user in local mode", func() {
printer.Clean()
prevLocal := viper.GetBool("local")
viper.Set("local", true)
defer viper.Set("local", prevLocal)
err := userStatusSetCmdF(s.client, newUserStatusSetCmd(), []string{model.StatusOnline})
s.Require().EqualError(err, "the --user flag is required in local mode")
s.Require().Len(printer.GetLines(), 0)
})
s.Run("Return error when status update fails", func() {
printer.Clean()
mockUser := model.User{Id: "me-id", Username: "me"}
wantStatus := &model.Status{UserId: "me-id", Status: model.StatusOnline}
s.client.
EXPECT().
GetMe(context.TODO(), "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateUserStatus(context.TODO(), "me-id", wantStatus).
Return(nil, &model.Response{}, errors.New("update error")).
Times(1)
err := userStatusSetCmdF(s.client, newUserStatusSetCmd(), []string{model.StatusOnline})
s.Require().EqualError(err, "could not set status for user me-id: update error")
s.Require().Len(printer.GetLines(), 0)
})
}