NOISSUE - Update Users tests (#2498)

Signed-off-by: Felix Gateru <felix.gateru@gmail.com>
This commit is contained in:
Felix Gateru
2024-11-04 11:42:09 +03:00
committed by GitHub
parent 0019f71b46
commit 233bbf9861
27 changed files with 2183 additions and 153 deletions
+4 -4
View File
@@ -148,7 +148,7 @@ paths:
patch:
operationId: updateUser
summary: Updates name and metadata of the user.
summary: Updates first, last name and metadata of the user.
description: |
Updates name and metadata of the user with provided ID. Name and metadata
is updated using authorization token and the new received info.
@@ -244,7 +244,7 @@ paths:
/users/{userID}/tags:
patch:
operationId: updateTags
summary: Updates tags the user.
summary: Updates tags of the user.
description: |
Updates tags of the user with provided ID. Tags is updated using
authorization token and the new tags received in request.
@@ -345,7 +345,7 @@ paths:
/users/{userID}/role:
patch:
operationId: updateRole
summary: Updates the user role.
summary: Updates the user's role.
description: |
Updates role for the user with provided ID.
tags:
@@ -441,7 +441,7 @@ paths:
/users/secret:
patch:
operationId: updateSecret
summary: Updates Secret of currently logged in user.
summary: Updates secret of currently logged in user.
description: |
Updates secret of currently logged in user. Secret is updated using
authorization token and the new received info.
+2 -2
View File
@@ -149,7 +149,7 @@ var cmdProvision = []cobra.Command{
return
}
ut, err := sdk.CreateToken(mgxsdk.Login{Username: user.Username, Secret: user.Credentials.Secret})
ut, err := sdk.CreateToken(mgxsdk.Login{Identity: user.Credentials.Username, Secret: user.Credentials.Secret})
if err != nil {
logErrorCmd(*cmd, err)
return
@@ -166,7 +166,7 @@ var cmdProvision = []cobra.Command{
return
}
ut, err = sdk.CreateToken(mgxsdk.Login{Email: user.Email, Secret: user.Credentials.Secret})
ut, err = sdk.CreateToken(mgxsdk.Login{Identity: user.Email, Secret: user.Credentials.Secret})
if err != nil {
logErrorCmd(*cmd, err)
return
+2 -2
View File
@@ -106,7 +106,7 @@ var cmdUsers = []cobra.Command{
}
loginReq := mgxsdk.Login{
Username: args[0],
Identity: args[0],
Secret: args[1],
}
@@ -189,7 +189,7 @@ var cmdUsers = []cobra.Command{
if args[0] == "username" {
user.ID = args[1]
user.Credentials.Username = args[2]
user, err := sdk.UpdateUser(user, args[3])
user, err := sdk.UpdateUsername(user, args[3])
if err != nil {
logErrorCmd(*cmd, err)
return
+2 -2
View File
@@ -338,8 +338,8 @@ func TestIssueTokenCmd(t *testing.T) {
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
lg := mgsdk.Login{
Email: tc.args[0],
Secret: tc.args[1],
Identity: tc.args[0],
Secret: tc.args[1],
}
sdkCall := sdkMock.On("CreateToken", lg).Return(tc.token, tc.sdkerr)
+3 -1
View File
@@ -172,7 +172,9 @@ func EncodeError(_ context.Context, err error, w http.ResponseWriter) {
errors.Contains(err, apiutil.ErrMissingUsername),
errors.Contains(err, apiutil.ErrMissingFirstName),
errors.Contains(err, apiutil.ErrMissingLastName),
errors.Contains(err, apiutil.ErrInvalidUsername):
errors.Contains(err, apiutil.ErrInvalidUsername),
errors.Contains(err, apiutil.ErrMissingIdentity),
errors.Contains(err, apiutil.ErrInvalidProfilePictureURL):
err = unwrap(err)
w.WriteHeader(http.StatusBadRequest)
+1 -1
View File
@@ -312,7 +312,7 @@ type SDK interface {
//
// example:
// lt := sdk.Login{
// Email: "john.doe@example",
// Identity: "email"/"username",
// Secret: "12345678",
// }
// token, _ := sdk.CreateToken(lt)
+1 -2
View File
@@ -20,8 +20,7 @@ type Token struct {
}
type Login struct {
Email string `json:"email"`
Username string `json:"username,omitempty"`
Identity string `json:"identity"`
Secret string `json:"secret"`
}
+10 -10
View File
@@ -41,7 +41,7 @@ func TestIssueToken(t *testing.T) {
{
desc: "issue token successfully",
login: sdk.Login{
Username: client.Credentials.Username,
Identity: client.Credentials.Username,
Secret: client.Credentials.Secret,
},
svcRes: &magistrala.Token{
@@ -54,9 +54,9 @@ func TestIssueToken(t *testing.T) {
err: nil,
},
{
desc: "issue token with invalid username",
desc: "issue token with invalid identity",
login: sdk.Login{
Username: invalidIdentity,
Identity: invalidIdentity,
Secret: client.Credentials.Secret,
},
svcRes: &magistrala.Token{},
@@ -67,7 +67,7 @@ func TestIssueToken(t *testing.T) {
{
desc: "issue token with invalid secret",
login: sdk.Login{
Username: client.Credentials.Username,
Identity: client.Credentials.Username,
Secret: "invalid",
},
svcRes: &magistrala.Token{},
@@ -76,20 +76,20 @@ func TestIssueToken(t *testing.T) {
err: errors.NewSDKErrorWithStatus(svcerr.ErrLogin, http.StatusUnauthorized),
},
{
desc: "issue token with empty username",
desc: "issue token with empty identity",
login: sdk.Login{
Username: "",
Identity: "",
Secret: client.Credentials.Secret,
},
svcRes: &magistrala.Token{},
svcErr: nil,
response: sdk.Token{},
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingUsername), http.StatusBadRequest),
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingIdentity), http.StatusBadRequest),
},
{
desc: "issue token with empty secret",
login: sdk.Login{
Username: client.Credentials.Username,
Identity: client.Credentials.Username,
Secret: "",
},
svcRes: &magistrala.Token{},
@@ -100,12 +100,12 @@ func TestIssueToken(t *testing.T) {
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
svcCall := svc.On("IssueToken", mock.Anything, tc.login.Username, tc.login.Secret).Return(tc.svcRes, tc.svcErr)
svcCall := svc.On("IssueToken", mock.Anything, tc.login.Identity, tc.login.Secret).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.CreateToken(tc.login)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "IssueToken", mock.Anything, tc.login.Username, tc.login.Secret)
ok := svcCall.Parent.AssertCalled(t, "IssueToken", mock.Anything, tc.login.Identity, tc.login.Secret)
assert.True(t, ok)
}
svcCall.Unset()
-1
View File
@@ -28,7 +28,6 @@ const (
// User represents magistrala user its credentials.
type User struct {
ID string `json:"id"`
Username string `json:"username,omitempty"`
FirstName string `json:"first_name,omitempty"`
LastName string `json:"last_name,omitempty"`
Email string `json:"email,omitempty"`
+391 -64
View File
@@ -59,7 +59,6 @@ func TestCreateUser(t *testing.T) {
createSdkUserReq := sdk.User{
FirstName: user.FirstName,
LastName: user.LastName,
Username: user.Username,
Email: user.Email,
Tags: user.Tags,
Credentials: user.Credentials,
@@ -1236,7 +1235,7 @@ func TestUpdateUserEmail(t *testing.T) {
err errors.SDKError
}{
{
desc: "update user Email with valid token",
desc: "update email with valid token",
token: validToken,
updateUserReq: sdk.User{
ID: user.ID,
@@ -1252,7 +1251,7 @@ func TestUpdateUserEmail(t *testing.T) {
err: nil,
},
{
desc: "update user Email with invalid token",
desc: "update email with invalid token",
token: invalidToken,
updateUserReq: sdk.User{
ID: user.ID,
@@ -1268,7 +1267,7 @@ func TestUpdateUserEmail(t *testing.T) {
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "update user Email with empty token",
desc: "update email with empty token",
token: "",
updateUserReq: sdk.User{
ID: user.ID,
@@ -1284,7 +1283,7 @@ func TestUpdateUserEmail(t *testing.T) {
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "update user Email with invalid id",
desc: "update email with invalid id",
token: validToken,
updateUserReq: sdk.User{
ID: wrongID,
@@ -1300,7 +1299,7 @@ func TestUpdateUserEmail(t *testing.T) {
err: errors.NewSDKErrorWithStatus(svcerr.ErrUpdateEntity, http.StatusUnprocessableEntity),
},
{
desc: "update user Email with empty id",
desc: "update email with empty id",
token: validToken,
updateUserReq: sdk.User{
ID: "",
@@ -1316,7 +1315,7 @@ func TestUpdateUserEmail(t *testing.T) {
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest),
},
{
desc: "update user Email with response that can't be unmarshalled",
desc: "update email with response that can't be unmarshalled",
token: validToken,
updateUserReq: sdk.User{
ID: user.ID,
@@ -1665,7 +1664,9 @@ func TestUpdateUserRole(t *testing.T) {
}
mgsdk := sdk.NewSDK(conf)
updatedUser := user
updatedRole := users.AdminRole.String()
updatedUser.Role = updatedRole
cases := []struct {
desc string
@@ -1679,23 +1680,23 @@ func TestUpdateUserRole(t *testing.T) {
response sdk.User
err errors.SDKError
}{
// {
// desc: "update user role with valid token",
// token: validToken,
// updateUserReq: sdk.User{
// ID: user.ID,
// Role: updatedRole,
// Email: user.Email,
// },
// svcReq: users.User{
// ID: user.ID,
// Role: users.AdminRole,
// },
// svcRes: convertUser(updatedUser),
// svcErr: nil,
// response: updatedUser,
// err: nil,
// },
{
desc: "update user role with valid token",
token: validToken,
updateUserReq: sdk.User{
ID: user.ID,
Role: updatedRole,
Email: user.Email,
},
svcReq: users.User{
ID: user.ID,
Role: users.AdminRole,
},
svcRes: convertUser(updatedUser),
svcErr: nil,
response: updatedUser,
err: nil,
},
{
desc: "update user role with invalid token",
token: invalidToken,
@@ -1725,22 +1726,22 @@ func TestUpdateUserRole(t *testing.T) {
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
// {
// desc: "update user role with invalid id",
// token: validToken,
// updateUserReq: sdk.User{
// ID: wrongID,
// Role: updatedRole,
// },
// svcReq: users.User{
// ID: wrongID,
// Role: users.AdminRole,
// },
// svcRes: users.User{},
// svcErr: svcerr.ErrUpdateEntity,
// response: sdk.User{},
// err: errors.NewSDKErrorWithStatus(svcerr.ErrUpdateEntity, http.StatusUnprocessableEntity),
// },
{
desc: "update user role with invalid id",
token: validToken,
updateUserReq: sdk.User{
ID: wrongID,
Role: updatedRole,
},
svcReq: users.User{
ID: wrongID,
Role: users.AdminRole,
},
svcRes: users.User{},
svcErr: svcerr.ErrUpdateEntity,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrUpdateEntity, http.StatusUnprocessableEntity),
},
{
desc: "update user role with empty id",
token: validToken,
@@ -1769,28 +1770,28 @@ func TestUpdateUserRole(t *testing.T) {
response: sdk.User{},
err: errors.NewSDKError(errors.New("json: unsupported type: chan int")),
},
// {
// desc: "update user role with response that can't be unmarshalled",
// token: validToken,
// updateUserReq: sdk.User{
// ID: user.ID,
// Role: updatedRole,
// },
// svcReq: users.User{
// ID: user.ID,
// Role: users.AdminRole,
// },
// svcRes: users.User{
// ID: id,
// Role: users.AdminRole,
// Metadata: users.Metadata{
// "key": make(chan int),
// },
// },
// svcErr: nil,
// response: sdk.User{},
// err: errors.NewSDKError(errors.New("unexpected end of JSON input")),
// },
{
desc: "update user role with response that can't be unmarshalled",
token: validToken,
updateUserReq: sdk.User{
ID: user.ID,
Role: updatedRole,
},
svcReq: users.User{
ID: user.ID,
Role: users.AdminRole,
},
svcRes: users.User{
ID: id,
Role: users.AdminRole,
Metadata: users.Metadata{
"key": make(chan int),
},
},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(errors.New("unexpected end of JSON input")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
@@ -1798,12 +1799,338 @@ func TestUpdateUserRole(t *testing.T) {
tc.session = mgauthn.Session{DomainUserID: validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("UpdateRole", mock.Anything, tc.session, tc.updateUserReq.ID, tc.svcReq).Return(tc.svcRes, tc.svcErr)
svcCall := svc.On("UpdateRole", mock.Anything, tc.session, tc.svcReq).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.UpdateUserRole(tc.updateUserReq, tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "UpdateRole", mock.Anything, tc.session, tc.updateUserReq.ID, tc.svcReq)
ok := svcCall.Parent.AssertCalled(t, "UpdateRole", mock.Anything, tc.session, tc.svcReq)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestUpdateUsername(t *testing.T) {
ts, svc, auth := setupUsers()
defer ts.Close()
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
updatedUser := user
updatedUsername := "updatedUsername"
updatedUser.Credentials.Username = updatedUsername
cases := []struct {
desc string
token string
session mgauthn.Session
updateUserReq sdk.User
svcReq users.User
svcRes users.User
svcErr error
authenticateErr error
response sdk.User
err errors.SDKError
}{
{
desc: "update username with valid token",
token: validToken,
updateUserReq: sdk.User{
ID: user.ID,
Credentials: sdk.Credentials{
Username: updatedUsername,
},
},
svcReq: users.User{
ID: user.ID,
Credentials: users.Credentials{
Username: updatedUsername,
},
},
svcRes: convertUser(updatedUser),
svcErr: nil,
response: updatedUser,
err: nil,
},
{
desc: "update username with invalid token",
token: invalidToken,
updateUserReq: sdk.User{
ID: user.ID,
Credentials: sdk.Credentials{
Username: updatedUsername,
},
},
svcReq: users.User{
ID: user.ID,
Credentials: users.Credentials{
Username: updatedUsername,
},
},
svcRes: users.User{},
authenticateErr: svcerr.ErrAuthentication,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "update username with empty token",
token: "",
updateUserReq: sdk.User{
ID: user.ID,
Credentials: sdk.Credentials{
Username: updatedUsername,
},
},
svcReq: users.User{},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "update username with invalid id",
token: validToken,
updateUserReq: sdk.User{
ID: wrongID,
Credentials: sdk.Credentials{
Username: updatedUsername,
},
},
svcReq: users.User{
ID: wrongID,
Credentials: users.Credentials{
Username: updatedUsername,
},
},
svcRes: users.User{},
svcErr: svcerr.ErrUpdateEntity,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrUpdateEntity, http.StatusUnprocessableEntity),
},
{
desc: "update username with empty id",
token: validToken,
updateUserReq: sdk.User{
ID: "",
Credentials: sdk.Credentials{
Username: updatedUsername,
},
},
svcReq: users.User{},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest),
},
{
desc: "update username with response that can't be unmarshalled",
token: validToken,
updateUserReq: sdk.User{
ID: user.ID,
Credentials: sdk.Credentials{
Username: updatedUsername,
},
},
svcReq: users.User{
ID: user.ID,
Credentials: users.Credentials{
Username: updatedUsername,
},
},
svcRes: users.User{
ID: id,
Credentials: users.Credentials{
Username: updatedUsername,
},
Metadata: users.Metadata{
"key": make(chan int),
},
},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(errors.New("unexpected end of JSON input")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = mgauthn.Session{DomainUserID: validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("UpdateUsername", mock.Anything, tc.session, tc.svcReq.ID, tc.svcReq.Credentials.Username).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.UpdateUsername(tc.updateUserReq, tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "UpdateUsername", mock.Anything, tc.session, tc.svcReq.ID, tc.svcReq.Credentials.Username)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestUpdateProfilePicture(t *testing.T) {
ts, svc, auth := setupUsers()
defer ts.Close()
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
updatedProfilePicture := "http://updated.com/profile.jpg"
updatedUser := user
updatedUser.Email = updatedProfilePicture
cases := []struct {
desc string
token string
session mgauthn.Session
updateUserReq sdk.User
svcReq users.User
svcRes users.User
svcErr error
authenticateErr error
response sdk.User
err errors.SDKError
}{
{
desc: "update profile picture with valid token",
token: validToken,
updateUserReq: sdk.User{
ID: user.ID,
ProfilePicture: updatedProfilePicture,
},
svcReq: users.User{
ID: user.ID,
ProfilePicture: updatedProfilePicture,
},
svcRes: convertUser(updatedUser),
svcErr: nil,
response: updatedUser,
err: nil,
},
{
desc: "update profile picture with invalid token",
token: invalidToken,
updateUserReq: sdk.User{
ID: user.ID,
ProfilePicture: updatedProfilePicture,
},
svcReq: users.User{
ID: user.ID,
ProfilePicture: updatedProfilePicture,
},
svcRes: users.User{},
authenticateErr: svcerr.ErrAuthentication,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "update profile picture with empty token",
token: "",
updateUserReq: sdk.User{
ID: user.ID,
ProfilePicture: updatedProfilePicture,
},
svcReq: users.User{
ID: user.ID,
ProfilePicture: updatedProfilePicture,
},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "update profile picture with invalid id",
token: validToken,
updateUserReq: sdk.User{
ID: wrongID,
ProfilePicture: updatedProfilePicture,
},
svcReq: users.User{
ID: wrongID,
ProfilePicture: updatedProfilePicture,
},
svcRes: users.User{},
svcErr: svcerr.ErrUpdateEntity,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrUpdateEntity, http.StatusUnprocessableEntity),
},
{
desc: "update profile picture with empty id",
token: validToken,
updateUserReq: sdk.User{
ID: "",
ProfilePicture: updatedProfilePicture,
},
svcReq: users.User{
ID: "",
ProfilePicture: updatedProfilePicture,
},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest),
},
{
desc: "update profile picture with request that can't be marshalled",
token: validToken,
updateUserReq: sdk.User{
ID: generateUUID(t),
Metadata: map[string]interface{}{
"test": make(chan int),
},
},
svcReq: users.User{},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(errors.New("json: unsupported type: chan int")),
},
{
desc: "update profile picture with response that can't be unmarshalled",
token: validToken,
updateUserReq: sdk.User{
ID: user.ID,
ProfilePicture: updatedProfilePicture,
},
svcReq: users.User{
ID: user.ID,
ProfilePicture: updatedProfilePicture,
},
svcRes: users.User{
ID: id,
Metadata: users.Metadata{
"key": make(chan int),
},
},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(errors.New("unexpected end of JSON input")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = mgauthn.Session{DomainUserID: validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("UpdateProfilePicture", mock.Anything, tc.session, tc.svcReq).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.UpdateProfilePicture(tc.updateUserReq, tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "UpdateProfilePicture", mock.Anything, tc.session, tc.svcReq)
assert.True(t, ok)
}
svcCall.Unset()
+1 -2
View File
@@ -294,8 +294,7 @@ func (ps *provisionService) createTokenIfEmpty(token string) (string, error) {
}
u := sdk.Login{
Email: ps.conf.Server.MgEmail,
Username: ps.conf.Server.MgUsername,
Identity: ps.conf.Server.MgUsername,
Secret: ps.conf.Server.MgPass,
}
tkn, err := ps.sdk.CreateToken(u)
+1 -1
View File
@@ -219,7 +219,7 @@ func TestCert(t *testing.T) {
mgsdk.On("IssueCert", c.thingID, c.config.Cert.TTL, c.domainID, mock.Anything).Return(sdk.Cert{SerialNumber: c.serial}, c.sdkCertErr)
mgsdk.On("ViewCert", c.serial, mock.Anything, mock.Anything).Return(sdk.Cert{Certificate: c.cert, Key: c.key}, c.sdkCertErr)
login := sdk.Login{
Username: c.config.Server.MgUsername,
Identity: c.config.Server.MgUsername,
Secret: c.config.Server.MgPass,
}
mgsdk.On("CreateToken", login).Return(sdk.Token{AccessToken: validToken}, c.sdkTokenErr)
+2 -2
View File
@@ -149,7 +149,7 @@ func createUser(s sdk.SDK, conf Config) (string, string, error) {
}
login := sdk.Login{
Username: user.Credentials.Username,
Identity: user.Credentials.Username,
Secret: user.Credentials.Secret,
}
token, err := s.CreateToken(login)
@@ -170,7 +170,7 @@ func createUser(s sdk.SDK, conf Config) (string, string, error) {
}
login = sdk.Login{
Username: user.Credentials.Username,
Identity: user.Credentials.Username,
Secret: user.Credentials.Secret,
}
token, err = s.CreateToken(login)
+2 -2
View File
@@ -95,7 +95,7 @@ func Provision(conf Config) error {
var err error
// Login user
token, err := s.CreateToken(sdk.Login{Username: user.Credentials.Username, Secret: user.Credentials.Secret})
token, err := s.CreateToken(sdk.Login{Identity: user.Credentials.Username, Secret: user.Credentials.Secret})
if err != nil {
return fmt.Errorf("unable to login user: %s", err.Error())
}
@@ -114,7 +114,7 @@ func Provision(conf Config) error {
}
// Login to domain
token, err = s.CreateToken(sdk.Login{
Username: user.Credentials.Username,
Identity: user.Credentials.Username,
Secret: user.Credentials.Secret,
})
if err != nil {
+399 -35
View File
@@ -264,6 +264,7 @@ func TestView(t *testing.T) {
status int
authnRes mgauthn.Session
authnErr error
svcErr error
err error
}{
{
@@ -300,6 +301,15 @@ func TestView(t *testing.T) {
authnRes: mgauthn.Session{UserID: validID, DomainID: domainID},
err: nil,
},
{
desc: "view user with invalid ID",
token: validToken,
id: inValid,
status: http.StatusBadRequest,
authnRes: mgauthn.Session{UserID: validID, DomainID: domainID},
svcErr: svcerr.ErrViewEntity,
err: svcerr.ErrViewEntity,
},
}
for _, tc := range cases {
@@ -312,7 +322,7 @@ func TestView(t *testing.T) {
}
authnCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
svcCall := svc.On("View", mock.Anything, tc.authnRes, tc.id).Return(users.User{}, tc.err)
svcCall := svc.On("View", mock.Anything, tc.authnRes, tc.id).Return(users.User{}, tc.svcErr)
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
var errRes respBody
@@ -340,6 +350,7 @@ func TestViewProfile(t *testing.T) {
status int
authnRes mgauthn.Session
authnErr error
svcErr error
err error
}{
{
@@ -368,6 +379,15 @@ func TestViewProfile(t *testing.T) {
authnRes: mgauthn.Session{},
err: apiutil.ErrBearerToken,
},
{
desc: "view profile with service error",
token: validToken,
id: user.ID,
status: http.StatusBadRequest,
authnRes: mgauthn.Session{UserID: validID, DomainID: domainID},
svcErr: svcerr.ErrViewEntity,
err: svcerr.ErrViewEntity,
},
}
for _, tc := range cases {
@@ -380,7 +400,7 @@ func TestViewProfile(t *testing.T) {
}
authnCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
svcCall := svc.On("ViewProfile", mock.Anything, tc.authnRes).Return(users.User{}, tc.err)
svcCall := svc.On("ViewProfile", mock.Anything, tc.authnRes).Return(users.User{}, tc.svcErr)
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
var errRes respBody
@@ -778,6 +798,7 @@ func TestSearchUsers(t *testing.T) {
query string
listUsersResponse users.UsersPage
authnErr error
svcErr error
err error
}{
{
@@ -865,6 +886,14 @@ func TestSearchUsers(t *testing.T) {
status: http.StatusBadRequest,
err: apiutil.ErrLenSearchQuery,
},
{
desc: "serach users with service error",
token: validToken,
query: "username=username",
status: http.StatusBadRequest,
svcErr: svcerr.ErrViewEntity,
err: svcerr.ErrViewEntity,
},
}
for _, tc := range cases {
@@ -882,7 +911,7 @@ func TestSearchUsers(t *testing.T) {
Page: tc.listUsersResponse.Page,
Users: tc.listUsersResponse.Users,
},
tc.err)
tc.svcErr)
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
@@ -1176,6 +1205,8 @@ func TestUpdateEmail(t *testing.T) {
us, svc, _, authn := newUsersServer()
defer us.Close()
newuseremail := "newuseremail@example.com"
cases := []struct {
desc string
data string
@@ -1185,14 +1216,15 @@ func TestUpdateEmail(t *testing.T) {
authnRes mgauthn.Session
authnErr error
status int
svcErr error
err error
}{
{
desc: "update user email as admin with valid token",
data: fmt.Sprintf(`{"email": "%s"}`, "newuseremail@example.com"),
data: fmt.Sprintf(`{"email": "%s"}`, newuseremail),
user: users.User{
ID: user.ID,
Email: "newuseremail@example.com",
Email: newuseremail,
Credentials: users.Credentials{
Secret: "secret",
},
@@ -1205,10 +1237,10 @@ func TestUpdateEmail(t *testing.T) {
},
{
desc: "update user email as normal user with valid token",
data: fmt.Sprintf(`{"email": "%s"}`, "newuseremail@example.com"),
data: fmt.Sprintf(`{"email": "%s"}`, newuseremail),
user: users.User{
ID: user.ID,
Email: "newuseremail@example.com",
Email: newuseremail,
Credentials: users.Credentials{
Secret: "secret",
},
@@ -1221,10 +1253,10 @@ func TestUpdateEmail(t *testing.T) {
},
{
desc: "update user email with empty token",
data: fmt.Sprintf(`{"email": "%s"}`, "newuseremail@example.com"),
data: fmt.Sprintf(`{"email": "%s"}`, newuseremail),
user: users.User{
ID: user.ID,
Email: "newuseremail@example.com",
Email: newuseremail,
Credentials: users.Credentials{
Secret: "secret",
},
@@ -1237,10 +1269,10 @@ func TestUpdateEmail(t *testing.T) {
},
{
desc: "update user email with invalid token",
data: fmt.Sprintf(`{"email": "%s"}`, "newuseremail@example.com"),
data: fmt.Sprintf(`{"email": "%s"}`, newuseremail),
user: users.User{
ID: user.ID,
Email: "newuseremail@example.com",
Email: newuseremail,
Credentials: users.Credentials{
Secret: "secret",
},
@@ -1253,10 +1285,10 @@ func TestUpdateEmail(t *testing.T) {
},
{
desc: "update user email with empty id",
data: fmt.Sprintf(`{"email": "%s"}`, "newuseremail@example.com"),
data: fmt.Sprintf(`{"email": "%s"}`, newuseremail),
user: users.User{
ID: "",
Email: "newuseremail@example.com",
Email: newuseremail,
Credentials: users.Credentials{
Secret: "secret",
},
@@ -1272,7 +1304,7 @@ func TestUpdateEmail(t *testing.T) {
data: fmt.Sprintf(`{"email": "%s"}`, ""),
user: users.User{
ID: user.ID,
Email: "newuseremail@example.com",
Email: newuseremail,
Credentials: users.Credentials{
Secret: "secret",
},
@@ -1297,6 +1329,20 @@ func TestUpdateEmail(t *testing.T) {
status: http.StatusBadRequest,
err: apiutil.ErrValidation,
},
{
desc: "update user email with service error",
data: fmt.Sprintf(`{"email": "%s"}`, newuseremail),
user: users.User{
ID: user.ID,
Email: newuseremail,
},
contentType: contentType,
token: validToken,
authnRes: mgauthn.Session{UserID: validID, DomainID: domainID},
status: http.StatusUnprocessableEntity,
svcErr: svcerr.ErrUpdateEntity,
err: svcerr.ErrUpdateEntity,
},
}
for _, tc := range cases {
@@ -1310,7 +1356,281 @@ func TestUpdateEmail(t *testing.T) {
}
authnCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
svcCall := svc.On("UpdateEmail", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(users.User{}, tc.err)
svcCall := svc.On("UpdateEmail", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.user, tc.svcErr)
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
var resBody respBody
err = json.NewDecoder(res.Body).Decode(&resBody)
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
if resBody.Err != "" || resBody.Message != "" {
err = errors.Wrap(errors.New(resBody.Err), errors.New(resBody.Message))
}
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
svcCall.Unset()
authnCall.Unset()
}
}
func TestUpdateUsername(t *testing.T) {
us, svc, _, authn := newUsersServer()
defer us.Close()
newusername := "newusername"
cases := []struct {
desc string
data string
user users.User
contentType string
token string
authnRes mgauthn.Session
authnErr error
status int
err error
}{
{
desc: "update username as admin with valid token",
data: fmt.Sprintf(`{"username": "%s"}`, newusername),
user: users.User{
ID: user.ID,
Credentials: users.Credentials{
Username: newusername,
},
},
contentType: contentType,
token: validToken,
authnRes: mgauthn.Session{UserID: validID, DomainID: domainID},
status: http.StatusOK,
err: nil,
},
{
desc: "update username with empty token",
data: fmt.Sprintf(`{"username": "%s"}`, newusername),
user: users.User{
ID: user.ID,
Credentials: users.Credentials{
Username: newusername,
},
},
contentType: contentType,
token: "",
status: http.StatusUnauthorized,
authnErr: svcerr.ErrAuthentication,
err: apiutil.ErrBearerToken,
},
{
desc: "update username with invalid token",
data: fmt.Sprintf(`{"username": "%s"}`, newusername),
user: users.User{
ID: user.ID,
Credentials: users.Credentials{
Username: newusername,
},
},
contentType: contentType,
token: inValid,
status: http.StatusUnauthorized,
authnErr: svcerr.ErrAuthentication,
err: svcerr.ErrAuthentication,
},
{
desc: "update username with empty id",
data: fmt.Sprintf(`{"username": "%s"}`, newusername),
user: users.User{
ID: "",
Credentials: users.Credentials{
Username: newusername,
},
},
contentType: contentType,
token: validToken,
authnRes: mgauthn.Session{UserID: validID, DomainID: validID},
status: http.StatusBadRequest,
err: apiutil.ErrMissingID,
},
{
desc: "update username with invalid contentype",
data: fmt.Sprintf(`{"username": "%s"}`, ""),
user: users.User{
ID: user.ID,
Credentials: users.Credentials{
Username: newusername,
},
},
contentType: "application/xml",
token: validToken,
status: http.StatusUnsupportedMediaType,
err: apiutil.ErrValidation,
},
{
desc: "update user email with malformed data",
data: fmt.Sprintf(`{"email": %s}`, "invalid"),
user: users.User{
ID: user.ID,
Credentials: users.Credentials{
Username: newusername,
},
},
token: validToken,
contentType: contentType,
status: http.StatusBadRequest,
err: apiutil.ErrValidation,
},
{
desc: "update username with invalid username",
data: fmt.Sprintf(`{"username": "%s"}`, "invalid"),
user: users.User{
ID: user.ID,
Credentials: users.Credentials{
Username: newusername,
},
},
contentType: contentType,
token: validToken,
status: http.StatusUnprocessableEntity,
err: svcerr.ErrUpdateEntity,
},
}
for _, tc := range cases {
req := testRequest{
user: us.Client(),
method: http.MethodPatch,
url: fmt.Sprintf("%s/users/%s/username", us.URL, tc.user.ID),
contentType: tc.contentType,
token: tc.token,
body: strings.NewReader(tc.data),
}
authnCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
svcCall := svc.On("UpdateUsername", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.user, tc.err)
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
var resBody respBody
err = json.NewDecoder(res.Body).Decode(&resBody)
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
if resBody.Err != "" || resBody.Message != "" {
err = errors.Wrap(errors.New(resBody.Err), errors.New(resBody.Message))
}
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
svcCall.Unset()
authnCall.Unset()
}
}
func TestUpdateProfilePicture(t *testing.T) {
us, svc, _, authn := newUsersServer()
defer us.Close()
newprofilepicture := "https://example.com/newprofilepicture"
cases := []struct {
desc string
data string
user users.User
contentType string
token string
authnRes mgauthn.Session
authnErr error
status int
svcErr error
err error
}{
{
desc: "update profile picture as admin with valid token",
data: fmt.Sprintf(`{"profile_picture": "%s"}`, newprofilepicture),
user: users.User{
ID: user.ID,
ProfilePicture: newprofilepicture,
},
contentType: contentType,
token: validToken,
authnRes: mgauthn.Session{UserID: validID, DomainID: domainID},
status: http.StatusOK,
err: nil,
},
{
desc: "update profile picture with empty token",
data: fmt.Sprintf(`{"profile_picture": "%s"}`, newprofilepicture),
user: users.User{},
contentType: contentType,
token: "",
status: http.StatusUnauthorized,
authnErr: svcerr.ErrAuthentication,
err: apiutil.ErrBearerToken,
},
{
desc: "update profile_picture with invalid token",
data: fmt.Sprintf(`{"profile_picture": "%s"}`, newprofilepicture),
user: users.User{},
contentType: contentType,
token: inValid,
status: http.StatusUnauthorized,
authnErr: svcerr.ErrAuthentication,
err: svcerr.ErrAuthentication,
},
{
desc: "update profile_picture with empty id",
data: fmt.Sprintf(`{"profile_picture": "%s"}`, newprofilepicture),
user: users.User{
ID: "",
ProfilePicture: newprofilepicture,
},
contentType: contentType,
token: validToken,
authnRes: mgauthn.Session{UserID: validID, DomainID: validID},
status: http.StatusBadRequest,
err: apiutil.ErrMissingID,
},
{
desc: "update profile_picture with invalid contentype",
data: fmt.Sprintf(`{"profile_picture": "%s"}`, ""),
user: users.User{
ID: user.ID,
ProfilePicture: newprofilepicture,
},
contentType: "application/xml",
token: validToken,
status: http.StatusUnsupportedMediaType,
err: apiutil.ErrValidation,
},
{
desc: "update profile picture with malformed data",
data: fmt.Sprintf(`{"profile_picture": %s}`, "invalid"),
user: users.User{},
token: validToken,
contentType: contentType,
status: http.StatusBadRequest,
err: apiutil.ErrValidation,
},
{
desc: "update profile picture with failed to update",
data: fmt.Sprintf(`{"profile_picture": "%s"}`, "invalid"),
user: users.User{
ID: user.ID,
},
contentType: contentType,
token: validToken,
status: http.StatusUnprocessableEntity,
svcErr: svcerr.ErrUpdateEntity,
err: svcerr.ErrUpdateEntity,
},
}
for _, tc := range cases {
req := testRequest{
user: us.Client(),
method: http.MethodPatch,
url: fmt.Sprintf("%s/users/%s/picture", us.URL, tc.user.ID),
contentType: tc.contentType,
token: tc.token,
body: strings.NewReader(tc.data),
}
authnCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
svcCall := svc.On("UpdateProfilePicture", mock.Anything, tc.authnRes, mock.Anything).Return(tc.user, tc.svcErr)
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
var resBody respBody
@@ -1438,6 +1758,7 @@ func TestPasswordReset(t *testing.T) {
status int
authnRes mgauthn.Session
authnErr error
svcErr error
err error
}{
{
@@ -1497,6 +1818,14 @@ func TestPasswordReset(t *testing.T) {
status: http.StatusUnsupportedMediaType,
err: apiutil.ErrValidation,
},
{
desc: "password reset with service error",
data: fmt.Sprintf(`{"token": "%s", "password": "%s", "confirm_password": "%s"}`, validToken, strongPass, strongPass),
token: validToken,
contentType: contentType,
status: http.StatusUnprocessableEntity,
svcErr: svcerr.ErrUpdateEntity,
},
}
for _, tc := range cases {
@@ -1511,7 +1840,7 @@ func TestPasswordReset(t *testing.T) {
body: strings.NewReader(tc.data),
}
authnCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
svcCall := svc.On("ResetSecret", mock.Anything, tc.authnRes, mock.Anything).Return(tc.err)
svcCall := svc.On("ResetSecret", mock.Anything, tc.authnRes, mock.Anything).Return(tc.svcErr)
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
@@ -1534,6 +1863,7 @@ func TestUpdateRole(t *testing.T) {
authnRes mgauthn.Session
authnErr error
status int
svcErr error
err error
}{
{
@@ -1606,6 +1936,17 @@ func TestUpdateRole(t *testing.T) {
status: http.StatusBadRequest,
err: apiutil.ErrValidation,
},
{
desc: "update user with service error",
data: fmt.Sprintf(`{"role": "%s"}`, "admin"),
userID: user.ID,
token: validToken,
authnRes: mgauthn.Session{UserID: validID, DomainID: domainID},
contentType: contentType,
status: http.StatusUnprocessableEntity,
svcErr: svcerr.ErrUpdateEntity,
err: svcerr.ErrUpdateEntity,
},
}
for _, tc := range cases {
@@ -1620,7 +1961,7 @@ func TestUpdateRole(t *testing.T) {
}
authnCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
svcCall := svc.On("UpdateRole", mock.Anything, tc.authnRes, mock.Anything).Return(users.User{}, tc.err)
svcCall := svc.On("UpdateRole", mock.Anything, tc.authnRes, mock.Anything).Return(users.User{}, tc.svcErr)
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
var resBody respBody
@@ -1790,50 +2131,43 @@ func TestIssueToken(t *testing.T) {
err error
}{
{
desc: "issue token with valid username and secret",
data: fmt.Sprintf(`{"username": "%s", "secret": "%s", "domainID": "%s"}`, validUsername, secret, validID),
desc: "issue token with valid identity and secret",
data: fmt.Sprintf(`{"identity": "%s", "secret": "%s"}`, validUsername, secret),
contentType: contentType,
status: http.StatusCreated,
err: nil,
},
{
desc: "issue token with empty username",
data: fmt.Sprintf(`{"username": "%s", "secret": "%s", "domainID": "%s"}`, "", secret, validID),
desc: "issue token with empty identity",
data: fmt.Sprintf(`{"identity": "%s", "secret": "%s"}`, "", secret),
contentType: contentType,
status: http.StatusBadRequest,
err: apiutil.ErrValidation,
},
{
desc: "issue token with empty secret",
data: fmt.Sprintf(`{"username": "%s", "secret": "%s", "domainID": "%s"}`, validUsername, "", validID),
contentType: contentType,
status: http.StatusBadRequest,
err: apiutil.ErrValidation,
},
{
desc: "issue token with empty domain",
data: fmt.Sprintf(`{"username": "%s", "secret": "%s", "domainID": "%s"}`, validUsername, secret, ""),
data: fmt.Sprintf(`{"identity": "%s", "secret": "%s"}`, validUsername, ""),
contentType: contentType,
status: http.StatusBadRequest,
err: apiutil.ErrValidation,
},
{
desc: "issue token with invalid email",
data: fmt.Sprintf(`{"username": "%s", "secret": "%s", "domainID": "%s"}`, "invalid", secret, validID),
data: fmt.Sprintf(`{"identity": "%s", "secret": "%s"}`, "invalid", secret),
contentType: contentType,
status: http.StatusUnauthorized,
err: svcerr.ErrAuthentication,
},
{
desc: "issues token with malformed data",
data: fmt.Sprintf(`{"username": %s, "secret": %s, "domainID": %s}`, validUsername, secret, validID),
data: fmt.Sprintf(`{"identity": %s, "secret": %s}`, validUsername, secret),
contentType: contentType,
status: http.StatusBadRequest,
err: apiutil.ErrValidation,
},
{
desc: "issue token with invalid contentype",
data: fmt.Sprintf(`{"username": "%s", "secret": "%s", "domainID": "%s"}`, "invalid", secret, validID),
data: fmt.Sprintf(`{"identity": "%s", "secret": "%s"}`, "invalid", secret),
contentType: "application/xml",
status: http.StatusUnsupportedMediaType,
err: apiutil.ErrValidation,
@@ -1979,6 +2313,7 @@ func TestEnable(t *testing.T) {
authnRes mgauthn.Session
authnErr error
status int
svcErr error
err error
}{
{
@@ -2023,6 +2358,15 @@ func TestEnable(t *testing.T) {
status: http.StatusBadRequest,
err: apiutil.ErrMissingID,
},
{
desc: "enable user with service error",
user: user,
token: validToken,
authnRes: mgauthn.Session{UserID: validID, DomainID: domainID},
status: http.StatusUnprocessableEntity,
svcErr: svcerr.ErrUpdateEntity,
err: svcerr.ErrUpdateEntity,
},
}
for _, tc := range cases {
@@ -2038,7 +2382,7 @@ func TestEnable(t *testing.T) {
}
authnCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
svcCall := svc.On("Enable", mock.Anything, tc.authnRes, mock.Anything).Return(users.User{}, tc.err)
svcCall := svc.On("Enable", mock.Anything, tc.authnRes, mock.Anything).Return(tc.user, tc.svcErr)
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
if tc.err != nil {
@@ -2069,6 +2413,7 @@ func TestDisable(t *testing.T) {
authnRes mgauthn.Session
authnErr error
status int
svcErr error
err error
}{
{
@@ -2113,6 +2458,15 @@ func TestDisable(t *testing.T) {
status: http.StatusBadRequest,
err: apiutil.ErrMissingID,
},
{
desc: "disable user with service error",
user: user,
token: validToken,
authnRes: mgauthn.Session{UserID: validID, DomainID: domainID},
status: http.StatusUnprocessableEntity,
svcErr: svcerr.ErrUpdateEntity,
err: svcerr.ErrUpdateEntity,
},
}
for _, tc := range cases {
@@ -2128,7 +2482,7 @@ func TestDisable(t *testing.T) {
}
authnCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
svcCall := svc.On("Disable", mock.Anything, mock.Anything, mock.Anything).Return(users.User{}, tc.err)
svcCall := svc.On("Disable", mock.Anything, mock.Anything, mock.Anything).Return(tc.user, tc.svcErr)
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
@@ -2150,6 +2504,7 @@ func TestDelete(t *testing.T) {
authnRes mgauthn.Session
authnErr error
status int
svcErr error
err error
}{
{
@@ -2181,6 +2536,15 @@ func TestDelete(t *testing.T) {
status: http.StatusMethodNotAllowed,
err: apiutil.ErrMissingID,
},
{
desc: "delete user with service error",
user: user,
token: validToken,
authnRes: mgauthn.Session{UserID: validID, DomainID: domainID},
status: http.StatusUnprocessableEntity,
svcErr: svcerr.ErrRemoveEntity,
err: svcerr.ErrRemoveEntity,
},
}
for _, tc := range cases {
@@ -2195,7 +2559,7 @@ func TestDelete(t *testing.T) {
body: strings.NewReader(data),
}
authnCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
repoCall := svc.On("Delete", mock.Anything, tc.authnRes, tc.user.ID).Return(tc.err)
repoCall := svc.On("Delete", mock.Anything, tc.authnRes, tc.user.ID).Return(tc.svcErr)
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
+2 -2
View File
@@ -433,7 +433,7 @@ func updateProfilePictureEndpoint(svc users.Service) endpoint.Endpoint {
return nil, svcerr.ErrAuthorization
}
user, err := svc.Update(ctx, session, user)
user, err := svc.UpdateProfilePicture(ctx, session, user)
if err != nil {
return nil, err
}
@@ -475,7 +475,7 @@ func issueTokenEndpoint(svc users.Service) endpoint.Endpoint {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
token, err := svc.IssueToken(ctx, req.Username, req.Secret)
token, err := svc.IssueToken(ctx, req.Identity, req.Secret)
if err != nil {
return nil, err
}
+7 -4
View File
@@ -214,7 +214,7 @@ func (req updateUserSecretReq) validate() error {
type updateUsernameReq struct {
id string
Username string
Username string `json:"username,omitempty"`
}
func (req updateUsernameReq) validate() error {
@@ -224,6 +224,9 @@ func (req updateUsernameReq) validate() error {
if len(req.Username) > api.MaxNameSize {
return apiutil.ErrNameSize
}
if req.Username == "" {
return apiutil.ErrMissingUsername
}
return nil
}
@@ -256,13 +259,13 @@ func (req changeUserStatusReq) validate() error {
}
type loginUserReq struct {
Username string `json:"username,omitempty"`
Identity string `json:"identity,omitempty"`
Secret string `json:"secret,omitempty"`
}
func (req loginUserReq) validate() error {
if req.Username == "" {
return apiutil.ErrMissingUsername
if req.Identity == "" {
return apiutil.ErrMissingIdentity
}
if req.Secret == "" {
return apiutil.ErrMissingPass
+6 -6
View File
@@ -509,26 +509,26 @@ func TestLoginUserReqValidate(t *testing.T) {
err error
}{
{
desc: "valid request with username",
desc: "valid request with identity",
req: loginUserReq{
Username: "example",
Identity: "example",
Secret: secret,
},
err: nil,
},
{
desc: "empty Username",
desc: "empty identity",
req: loginUserReq{
Username: "",
Identity: "",
Secret: secret,
},
err: apiutil.ErrMissingUsername,
err: apiutil.ErrMissingIdentity,
},
{
desc: "empty secret",
req: loginUserReq{
Secret: "",
Username: "example",
Identity: "example",
},
err: apiutil.ErrMissingPass,
},
+2 -2
View File
@@ -107,7 +107,7 @@ func (es *eventStore) UpdateUsername(ctx context.Context, session authn.Session,
}
func (es *eventStore) UpdateProfilePicture(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
user, err := es.svc.Update(ctx, session, user)
user, err := es.svc.UpdateProfilePicture(ctx, session, user)
if err != nil {
return user, err
}
@@ -120,7 +120,7 @@ func (es *eventStore) UpdateProfilePicture(ctx context.Context, session authn.Se
return user, err
}
return es.update(ctx, "profile_picture", user)
return user, nil
}
func (es *eventStore) UpdateEmail(ctx context.Context, session authn.Session, id, email string) (users.User, error) {
+3
View File
@@ -124,6 +124,9 @@ func (am *authorizationMiddleware) UpdateUsername(ctx context.Context, session a
}
func (am *authorizationMiddleware) UpdateProfilePicture(ctx context.Context, session authn.Session, user users.User) (users.User, error) {
if err := am.checkSuperAdmin(ctx, session.UserID); err == nil {
session.SuperAdmin = true
}
return am.svc.UpdateProfilePicture(ctx, session, user)
}
+3 -2
View File
@@ -286,7 +286,8 @@ func (lm *loggingMiddleware) UpdateProfilePicture(ctx context.Context, session a
args := []any{
slog.String("duration", time.Since(begin).String()),
slog.Group("user",
slog.String("id", u.ID),
slog.String("id", user.ID),
slog.String("profile_picture", user.ProfilePicture),
),
}
if err != nil {
@@ -296,7 +297,7 @@ func (lm *loggingMiddleware) UpdateProfilePicture(ctx context.Context, session a
}
lm.logger.Info("Update profile picture completed successfully", args...)
}(time.Now())
return lm.svc.Update(ctx, session, user)
return lm.svc.UpdateProfilePicture(ctx, session, user)
}
// GenerateResetToken logs the generate_reset_token request. It logs the time it took to complete the request.
+1 -1
View File
@@ -144,7 +144,7 @@ func (ms *metricsMiddleware) UpdateProfilePicture(ctx context.Context, session a
ms.counter.With("method", "update_profile_picture").Add(1)
ms.latency.With("method", "update_profile_picture").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.Update(ctx, session, user)
return ms.svc.UpdateProfilePicture(ctx, session, user)
}
// GenerateResetToken instruments GenerateResetToken method with metrics.
+3 -3
View File
@@ -178,7 +178,7 @@ func (repo *userRepo) UpdateUsername(ctx context.Context, user users.User) (user
row, err := repo.Repository.DB.NamedQueryContext(ctx, q, dbu)
if err != nil {
return users.User{}, postgres.HandleError(err, repoerr.ErrUpdateEntity)
return users.User{}, postgres.HandleError(repoerr.ErrUpdateEntity, err)
}
defer row.Close()
@@ -233,7 +233,7 @@ func (repo *userRepo) Update(ctx context.Context, user users.User) (users.User,
q := fmt.Sprintf(`UPDATE users SET %s updated_at = :updated_at, updated_by = :updated_by
WHERE id = :id AND status = :status
RETURNING id, tags, metadata, status, created_at, updated_at, updated_by, last_name, first_name, username, profile_picture, email`, upq)
RETURNING id, tags, metadata, status, created_at, updated_at, updated_by, last_name, first_name, username, profile_picture, email, role`, upq)
user.Status = users.EnabledStatus
return repo.update(ctx, user, q)
@@ -388,7 +388,7 @@ func (repo *userRepo) RetrieveAllByIDs(ctx context.Context, pm users.Page) (user
items = append(items, c)
}
cq := fmt.Sprintf(`SELECT COUNT(*) FROM clients c %s;`, query)
cq := fmt.Sprintf(`SELECT COUNT(*) FROM users u %s;`, query)
total, err := postgres.Total(ctx, repo.Repository.DB, cq, dbPage)
if err != nil {
File diff suppressed because it is too large Load Diff
+138
View File
@@ -953,6 +953,144 @@ func TestUpdateEmail(t *testing.T) {
}
}
func TestUpdateProfilePicture(t *testing.T) {
svc, cRepo := newServiceMinimal()
user.ProfilePicture = "https://example.com/profile.jpg"
adminID := testsutil.GenerateUUID(t)
cases := []struct {
desc string
user users.User
session authn.Session
updateProfilePicResponse users.User
updateProfilePicErr error
checkSuperAdminErr error
err error
}{
{
desc: "update profile picture as normal user successfully",
user: user,
session: authn.Session{UserID: user.ID},
updateProfilePicResponse: user,
err: nil,
},
{
desc: "update profile picture as normal user with repo error on update",
user: user,
session: authn.Session{UserID: user.ID},
updateProfilePicResponse: users.User{},
updateProfilePicErr: errors.ErrMalformedEntity,
err: svcerr.ErrUpdateEntity,
},
{
desc: "update profile picture as admin successfully",
user: user,
session: authn.Session{UserID: adminID, SuperAdmin: true},
err: nil,
},
{
desc: "update profile picture as admin with failed check on super admin",
user: user,
session: authn.Session{UserID: adminID},
checkSuperAdminErr: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
},
{
desc: "update profile picture as admin with repo error on update",
user: user,
session: authn.Session{UserID: adminID, SuperAdmin: true},
updateProfilePicResponse: users.User{},
updateProfilePicErr: errors.ErrMalformedEntity,
err: svcerr.ErrUpdateEntity,
},
}
for _, tc := range cases {
repoCall := cRepo.On("CheckSuperAdmin", context.Background(), mock.Anything).Return(tc.checkSuperAdminErr)
repoCall1 := cRepo.On("Update", context.Background(), mock.Anything).Return(tc.updateProfilePicResponse, tc.updateProfilePicErr)
updatedUser, err := svc.UpdateProfilePicture(context.Background(), tc.session, tc.user)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
assert.Equal(t, tc.updateProfilePicResponse, updatedUser, fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.updateProfilePicResponse, updatedUser))
if tc.err == nil {
ok := repoCall1.Parent.AssertCalled(t, "Update", context.Background(), mock.Anything)
assert.True(t, ok, fmt.Sprintf("Update was not called on %s", tc.desc))
}
repoCall.Unset()
repoCall1.Unset()
}
}
func TestUpdateUsername(t *testing.T) {
svc, cRepo := newServiceMinimal()
nuser := user
nuser.Credentials.Username = "newusername"
adminID := testsutil.GenerateUUID(t)
cases := []struct {
desc string
user users.User
session authn.Session
updateUsernameResponse users.User
updateUsernameErr error
checkSuperAdminErr error
err error
}{
{
desc: "update username as normal user successfully",
user: user,
session: authn.Session{UserID: user.ID},
updateUsernameResponse: nuser,
err: nil,
},
{
desc: "update username as normal user with repo error on update",
user: user,
session: authn.Session{UserID: user.ID},
updateUsernameResponse: users.User{},
updateUsernameErr: errors.ErrMalformedEntity,
err: svcerr.ErrUpdateEntity,
},
{
desc: "update username as admin successfully",
user: user,
session: authn.Session{UserID: adminID, SuperAdmin: true},
updateUsernameResponse: nuser,
err: nil,
},
{
desc: "update username as admin with failed check on super admin",
user: user,
session: authn.Session{UserID: adminID},
checkSuperAdminErr: svcerr.ErrAuthorization,
err: svcerr.ErrAuthorization,
},
{
desc: "update username as admin with repo error on update",
user: user,
session: authn.Session{UserID: adminID, SuperAdmin: true},
updateUsernameResponse: users.User{},
updateUsernameErr: errors.ErrMalformedEntity,
err: svcerr.ErrUpdateEntity,
},
}
for _, tc := range cases {
repoCall := cRepo.On("CheckSuperAdmin", context.Background(), mock.Anything).Return(tc.checkSuperAdminErr)
repoCall1 := cRepo.On("UpdateUsername", context.Background(), mock.Anything).Return(tc.updateUsernameResponse, tc.updateUsernameErr)
updatedUser, err := svc.UpdateUsername(context.Background(), tc.session, tc.user.ID, tc.user.Credentials.Username)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
assert.Equal(t, tc.updateUsernameResponse, updatedUser, fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.updateUsernameResponse, updatedUser))
if tc.err == nil {
ok := repoCall1.Parent.AssertCalled(t, "UpdateUsername", context.Background(), mock.Anything)
assert.True(t, ok, fmt.Sprintf("UpdateUsername was not called on %s", tc.desc))
}
repoCall.Unset()
repoCall1.Unset()
}
}
func TestEnableUser(t *testing.T) {
svc, cRepo := newServiceMinimal()
+1 -1
View File
@@ -142,7 +142,7 @@ func (tm *tracingMiddleware) UpdateProfilePicture(ctx context.Context, session a
ctx, span := tm.tracer.Start(ctx, "svc_update_profile_picture", trace.WithAttributes(attribute.String("id", usr.ID)))
defer span.End()
return tm.svc.Update(ctx, session, usr)
return tm.svc.UpdateProfilePicture(ctx, session, usr)
}
// GenerateResetToken traces the "GenerateResetToken" operation of the wrapped users.Service.
+1 -1
View File
@@ -169,7 +169,7 @@ type Service interface {
// UpdateUsername updates the user's username.
UpdateUsername(ctx context.Context, session authn.Session, id, username string) (User, error)
// UpdateProfile updates the user's profile picture.
// UpdateProfilePicture updates the user's profile picture.
UpdateProfilePicture(ctx context.Context, session authn.Session, user User) (User, error)
// GenerateResetToken email where mail will be sent.