Files
magistrala/pkg/sdk/users_test.go
T
2026-03-04 12:37:35 +01:00

2495 lines
67 KiB
Go

// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package sdk_test
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
grpcTokenV1 "github.com/absmach/supermq/api/grpc/token/v1"
api "github.com/absmach/supermq/api/http"
apiutil "github.com/absmach/supermq/api/http/util"
authmocks "github.com/absmach/supermq/auth/mocks"
smqlog "github.com/absmach/supermq/logger"
smqauthn "github.com/absmach/supermq/pkg/authn"
authnmocks "github.com/absmach/supermq/pkg/authn/mocks"
"github.com/absmach/supermq/pkg/errors"
svcerr "github.com/absmach/supermq/pkg/errors/service"
oauth2mocks "github.com/absmach/supermq/pkg/oauth2/mocks"
sdk "github.com/absmach/supermq/pkg/sdk"
"github.com/absmach/supermq/pkg/uuid"
"github.com/absmach/supermq/users"
httpapi "github.com/absmach/supermq/users/api"
umocks "github.com/absmach/supermq/users/mocks"
"github.com/go-chi/chi/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
var (
id = generateUUID(&testing.T{})
domainID = "c717fa97-ffd9-40cb-8cf9-7c2859059395"
)
func setupUsers() (*httptest.Server, *umocks.Service, *authnmocks.Authentication) {
usvc := new(umocks.Service)
logger := smqlog.NewMock()
mux := chi.NewRouter()
idp := uuid.NewMock()
provider := new(oauth2mocks.Provider)
provider.On("Name").Return("test")
authn := new(authnmocks.Authentication)
am := smqauthn.NewAuthNMiddleware(authn, smqauthn.WithDomainCheck(false), smqauthn.WithAllowUnverifiedUser(true))
token := new(authmocks.TokenServiceClient)
httpapi.MakeHandler(usvc, am, token, true, mux, logger, "", passRegex, idp, provider)
return httptest.NewServer(mux), usvc, authn
}
func TestCreateUser(t *testing.T) {
ts, svc, _ := setupUsers()
defer ts.Close()
createSdkUserReq := sdk.User{
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email,
Tags: user.Tags,
Credentials: user.Credentials,
Metadata: user.Metadata,
PrivateMetadata: user.PrivateMetadata,
Status: user.Status,
}
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
cases := []struct {
desc string
token string
createSdkUserReq sdk.User
svcReq users.User
svcRes users.User
svcErr error
response sdk.User
err errors.SDKError
}{
{
desc: "register new user successfully",
token: validToken,
createSdkUserReq: createSdkUserReq,
svcReq: convertUser(createSdkUserReq),
svcRes: convertUser(user),
svcErr: nil,
response: user,
err: nil,
},
{
desc: "register existing user",
token: validToken,
createSdkUserReq: createSdkUserReq,
svcReq: convertUser(createSdkUserReq),
svcRes: users.User{},
svcErr: svcerr.ErrCreateEntity,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrCreateEntity, http.StatusUnprocessableEntity),
},
{
desc: "register user with invalid token",
token: invalidToken,
createSdkUserReq: createSdkUserReq,
svcReq: convertUser(createSdkUserReq),
svcRes: users.User{},
svcErr: svcerr.ErrAuthentication,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "register user with empty token",
token: "",
createSdkUserReq: createSdkUserReq,
svcReq: convertUser(createSdkUserReq),
svcRes: users.User{},
svcErr: svcerr.ErrAuthentication,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "register empty credentials user",
token: validToken,
createSdkUserReq: sdk.User{
FirstName: createSdkUserReq.FirstName,
LastName: createSdkUserReq.LastName,
Email: createSdkUserReq.Email,
Credentials: sdk.Credentials{
Username: "",
Secret: "",
},
},
svcReq: users.User{},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingUsername, http.StatusBadRequest),
},
{
desc: "register user with first name too long",
token: validToken,
createSdkUserReq: sdk.User{
FirstName: strings.Repeat("a", 1025),
Credentials: createSdkUserReq.Credentials,
PrivateMetadata: createSdkUserReq.PrivateMetadata,
Metadata: createSdkUserReq.Metadata,
Tags: createSdkUserReq.Tags,
},
svcReq: users.User{},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrNameSize, http.StatusBadRequest),
},
{
desc: "register user with empty userName",
token: validToken,
createSdkUserReq: sdk.User{
FirstName: createSdkUserReq.FirstName,
LastName: createSdkUserReq.LastName,
Email: createSdkUserReq.Email,
Credentials: sdk.Credentials{
Username: "",
Secret: createSdkUserReq.Credentials.Secret,
},
PrivateMetadata: createSdkUserReq.PrivateMetadata,
Metadata: createSdkUserReq.Metadata,
Tags: createSdkUserReq.Tags,
},
svcReq: users.User{},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingUsername, http.StatusBadRequest),
},
{
desc: "register user with empty secret",
token: validToken,
createSdkUserReq: sdk.User{
FirstName: createSdkUserReq.FirstName,
LastName: createSdkUserReq.LastName,
Email: createSdkUserReq.Email,
Credentials: sdk.Credentials{
Username: createSdkUserReq.Credentials.Username,
Secret: "",
},
PrivateMetadata: createSdkUserReq.PrivateMetadata,
Metadata: createSdkUserReq.Metadata,
Tags: createSdkUserReq.Tags,
},
svcReq: users.User{},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingPass, http.StatusBadRequest),
},
{
desc: "register user with secret that is too short",
token: validToken,
createSdkUserReq: sdk.User{
FirstName: createSdkUserReq.FirstName,
LastName: createSdkUserReq.LastName,
Email: createSdkUserReq.Email,
Credentials: sdk.Credentials{
Username: createSdkUserReq.Credentials.Username,
Secret: "weak",
},
PrivateMetadata: createSdkUserReq.PrivateMetadata,
Metadata: createSdkUserReq.Metadata,
Tags: createSdkUserReq.Tags,
},
svcReq: users.User{},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrPasswordFormat, http.StatusBadRequest),
},
{
desc: "register a user with request that can't be marshalled",
token: validToken,
createSdkUserReq: sdk.User{
Credentials: sdk.Credentials{
Username: "user",
Secret: "12345678",
},
FirstName: createSdkUserReq.FirstName,
LastName: createSdkUserReq.LastName,
Email: createSdkUserReq.Email,
Metadata: map[string]any{
"test": make(chan int),
},
},
svcReq: users.User{},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(fmt.Errorf("json: unsupported type: chan int")),
},
{
desc: "register a user with response that can't be unmarshalled",
token: validToken,
createSdkUserReq: createSdkUserReq,
svcReq: convertUser(createSdkUserReq),
svcRes: users.User{
ID: id,
FirstName: createSdkUserReq.FirstName,
LastName: createSdkUserReq.LastName,
Email: createSdkUserReq.Email,
Credentials: users.Credentials{
Username: createSdkUserReq.Credentials.Username,
Secret: createSdkUserReq.Credentials.Secret,
},
Metadata: users.Metadata{
"key": make(chan int),
},
},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(fmt.Errorf("unexpected end of JSON input")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
svcCall := svc.On("Register", mock.Anything, smqauthn.Session{}, tc.svcReq, true).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.CreateUser(context.Background(), tc.createSdkUserReq, tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "Register", mock.Anything, smqauthn.Session{}, tc.svcReq, true)
assert.True(t, ok)
}
svcCall.Unset()
})
}
}
func TestListUsers(t *testing.T) {
ts, svc, auth := setupUsers()
defer ts.Close()
var cls []sdk.User
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
for i := 10; i < 100; i++ {
cl := sdk.User{
ID: generateUUID(t),
FirstName: fmt.Sprintf("user_%d", i),
Credentials: sdk.Credentials{
Username: fmt.Sprintf("Username_%d", i),
Secret: fmt.Sprintf("password_%d", i),
},
Metadata: sdk.Metadata{"name": fmt.Sprintf("user_%d", i)},
Status: users.EnabledStatus.String(),
Role: users.UserRole.String(),
}
if i == 50 {
cl.Status = users.DisabledStatus.String()
cl.Tags = []string{"tag1", "tag2"}
}
cls = append(cls, cl)
}
cases := []struct {
desc string
token string
session smqauthn.Session
pageMeta sdk.PageMetadata
svcReq users.Page
svcRes users.UsersPage
svcErr error
authenticateErr error
response sdk.UsersPage
err errors.SDKError
}{
{
desc: "list users successfully",
token: validToken,
pageMeta: sdk.PageMetadata{
Offset: offset,
Limit: limit,
},
svcReq: users.Page{
Offset: offset,
Limit: limit,
Order: api.DefOrder,
Dir: api.DefDir,
},
svcRes: users.UsersPage{
Page: users.Page{
Total: uint64(len(cls[offset:limit])),
},
Users: convertUsers(cls[offset:limit]),
},
response: sdk.UsersPage{
PageRes: sdk.PageRes{
Total: uint64(len(cls[offset:limit])),
},
Users: cls[offset:limit],
},
err: nil,
},
{
desc: "list users with invalid token",
token: invalidToken,
session: smqauthn.Session{},
pageMeta: sdk.PageMetadata{
Offset: offset,
Limit: limit,
},
svcReq: users.Page{
Offset: offset,
Limit: limit,
Order: api.DefOrder,
Dir: api.DefDir,
},
svcRes: users.UsersPage{},
svcErr: svcerr.ErrAuthentication,
authenticateErr: svcerr.ErrAuthentication,
response: sdk.UsersPage{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "list users with empty token",
token: "",
pageMeta: sdk.PageMetadata{
Offset: offset,
Limit: limit,
},
svcReq: users.Page{},
svcRes: users.UsersPage{},
svcErr: nil,
authenticateErr: apiutil.ErrBearerToken,
response: sdk.UsersPage{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "list users with zero limit",
token: validToken,
pageMeta: sdk.PageMetadata{
Offset: offset,
Limit: 0,
},
svcReq: users.Page{
Offset: offset,
Limit: 10,
Order: api.DefOrder,
Dir: api.DefDir,
},
svcRes: users.UsersPage{
Page: users.Page{
Total: uint64(len(cls[offset:10])),
},
Users: convertUsers(cls[offset:10]),
},
response: sdk.UsersPage{
PageRes: sdk.PageRes{
Total: uint64(len(cls[offset:10])),
},
Users: cls[offset:10],
},
err: nil,
},
{
desc: "list users with limit greater than max",
token: validToken,
pageMeta: sdk.PageMetadata{
Offset: offset,
Limit: 101,
},
svcReq: users.Page{},
svcRes: users.UsersPage{},
svcErr: nil,
response: sdk.UsersPage{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrLimitSize, http.StatusBadRequest),
},
{
desc: "list users with given metadata",
token: validToken,
pageMeta: sdk.PageMetadata{
Offset: offset,
Limit: limit,
Metadata: sdk.Metadata{"name": "user_99"},
},
svcReq: users.Page{
Offset: offset,
Limit: limit,
Metadata: users.Metadata{"name": "user_99"},
Order: api.DefOrder,
Dir: api.DefDir,
},
svcRes: users.UsersPage{
Page: users.Page{
Total: 1,
},
Users: []users.User{convertUser(cls[89])},
},
svcErr: nil,
response: sdk.UsersPage{
PageRes: sdk.PageRes{
Total: 1,
},
Users: []sdk.User{cls[89]},
},
err: nil,
},
{
desc: "list users with given status",
token: validToken,
pageMeta: sdk.PageMetadata{
Offset: offset,
Limit: limit,
Status: users.DisabledStatus.String(),
},
svcReq: users.Page{
Offset: offset,
Limit: limit,
Status: users.DisabledStatus,
Order: api.DefOrder,
Dir: api.DefDir,
},
svcRes: users.UsersPage{
Page: users.Page{
Total: 1,
},
Users: []users.User{convertUser(cls[50])},
},
svcErr: nil,
response: sdk.UsersPage{
PageRes: sdk.PageRes{
Total: 1,
},
Users: []sdk.User{cls[50]},
},
err: nil,
},
{
desc: "list users with given tag",
token: validToken,
pageMeta: sdk.PageMetadata{
Offset: offset,
Limit: limit,
Tags: sdk.TagsQuery{Elements: []string{"tag1"}, Operator: sdk.OrOp},
},
svcReq: users.Page{
Offset: offset,
Limit: limit,
Tags: users.TagsQuery{Elements: []string{"tag1"}, Operator: users.OrOp},
Order: api.DefOrder,
Dir: api.DefDir,
},
svcRes: users.UsersPage{
Page: users.Page{
Total: 1,
},
Users: []users.User{convertUser(cls[50])},
},
svcErr: nil,
response: sdk.UsersPage{
PageRes: sdk.PageRes{
Total: 1,
},
Users: []sdk.User{cls[50]},
},
err: nil,
},
{
desc: "list users with CreatedFrom",
token: validToken,
pageMeta: sdk.PageMetadata{
Offset: offset,
Limit: limit,
CreatedFrom: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
},
svcReq: users.Page{
Offset: offset,
Limit: limit,
CreatedFrom: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
Order: api.DefOrder,
Dir: api.DefDir,
},
svcRes: users.UsersPage{
Page: users.Page{
Total: uint64(len(cls[offset:limit])),
},
Users: convertUsers(cls[offset:limit]),
},
svcErr: nil,
response: sdk.UsersPage{
PageRes: sdk.PageRes{
Total: uint64(len(cls[offset:limit])),
},
Users: cls[offset:limit],
},
err: nil,
},
{
desc: "list users with CreatedTo",
token: validToken,
pageMeta: sdk.PageMetadata{
Offset: offset,
Limit: limit,
CreatedTo: time.Date(2025, 12, 31, 23, 59, 59, 0, time.UTC),
},
svcReq: users.Page{
Offset: offset,
Limit: limit,
CreatedTo: time.Date(2025, 12, 31, 23, 59, 59, 0, time.UTC),
Order: api.DefOrder,
Dir: api.DefDir,
},
svcRes: users.UsersPage{
Page: users.Page{
Total: uint64(len(cls[offset:limit])),
},
Users: convertUsers(cls[offset:limit]),
},
svcErr: nil,
response: sdk.UsersPage{
PageRes: sdk.PageRes{
Total: uint64(len(cls[offset:limit])),
},
Users: cls[offset:limit],
},
err: nil,
},
{
desc: "list users with both CreatedFrom and CreatedTo",
token: validToken,
pageMeta: sdk.PageMetadata{
Offset: offset,
Limit: limit,
CreatedFrom: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
CreatedTo: time.Date(2025, 12, 31, 23, 59, 59, 0, time.UTC),
},
svcReq: users.Page{
Offset: offset,
Limit: limit,
CreatedFrom: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
CreatedTo: time.Date(2025, 12, 31, 23, 59, 59, 0, time.UTC),
Order: api.DefOrder,
Dir: api.DefDir,
},
svcRes: users.UsersPage{
Page: users.Page{
Total: 2,
},
Users: []users.User{convertUser(cls[10]), convertUser(cls[20])},
},
svcErr: nil,
response: sdk.UsersPage{
PageRes: sdk.PageRes{
Total: 2,
},
Users: []sdk.User{cls[10], cls[20]},
},
err: nil,
},
{
desc: "list users with request that can't be marshalled",
token: validToken,
pageMeta: sdk.PageMetadata{
Offset: offset,
Limit: limit,
Metadata: sdk.Metadata{
"test": make(chan int),
},
},
svcReq: users.Page{
Offset: offset,
Limit: limit,
Order: api.DefOrder,
Dir: api.DefDir,
},
svcRes: users.UsersPage{},
svcErr: nil,
response: sdk.UsersPage{},
err: errors.NewSDKError(fmt.Errorf("json: unsupported type: chan int")),
},
{
desc: "list users with response that can't be unmarshalled",
token: validToken,
pageMeta: sdk.PageMetadata{
Offset: offset,
Limit: limit,
},
svcReq: users.Page{
Offset: offset,
Limit: limit,
Order: api.DefOrder,
Dir: api.DefDir,
},
svcRes: users.UsersPage{
Page: users.Page{
Total: uint64(len(cls[offset:limit])),
},
Users: []users.User{
{
ID: id,
FirstName: "user_99",
Metadata: users.Metadata{
"key": make(chan int),
},
},
},
},
response: sdk.UsersPage{},
err: errors.NewSDKError(fmt.Errorf("unexpected end of JSON input")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("ListUsers", mock.Anything, tc.session, tc.svcReq).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.Users(context.Background(), tc.pageMeta, tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.token != "" {
ok := authCall.Parent.AssertCalled(t, "Authenticate", mock.Anything, tc.token)
assert.True(t, ok)
}
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "ListUsers", mock.Anything, tc.session, tc.svcReq)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestSearchUsers(t *testing.T) {
ts, svc, auth := setupUsers()
defer ts.Close()
var cls []sdk.User
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
for i := 10; i < 100; i++ {
cl := sdk.User{
ID: generateUUID(t),
FirstName: fmt.Sprintf("user_%d", i),
Email: fmt.Sprintf("email_%d", i),
Credentials: sdk.Credentials{
Username: fmt.Sprintf("Username_%d", i),
Secret: fmt.Sprintf("password_%d", i),
},
Metadata: sdk.Metadata{"name": fmt.Sprintf("user_%d", i)},
Status: users.EnabledStatus.String(),
Role: users.UserRole.String(),
}
if i == 50 {
cl.Status = users.DisabledStatus.String()
cl.Tags = []string{"tag1", "tag2"}
}
cls = append(cls, cl)
}
cases := []struct {
desc string
token string
page sdk.PageMetadata
response []sdk.User
searchreturn users.UsersPage
err errors.SDKError
authenticateErr error
}{
{
desc: "search for users",
token: validToken,
err: nil,
page: sdk.PageMetadata{
Offset: offset,
Limit: limit,
Username: "user_20",
},
response: []sdk.User{cls[10]},
searchreturn: users.UsersPage{
Users: []users.User{convertUser(cls[10])},
Page: users.Page{
Total: 1,
Offset: offset,
Limit: limit,
},
},
},
{
desc: "search for users with invalid token",
token: invalidToken,
page: sdk.PageMetadata{
Offset: offset,
Limit: limit,
Username: "user_10",
},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
response: nil,
authenticateErr: svcerr.ErrAuthentication,
},
{
desc: "search for users with empty token",
token: "",
page: sdk.PageMetadata{
Offset: offset,
Limit: limit,
Username: "user_10",
},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
response: nil,
authenticateErr: svcerr.ErrAuthentication,
},
{
desc: "search for users with empty query",
token: validToken,
page: sdk.PageMetadata{
Offset: offset,
Limit: limit,
FirstName: "",
},
err: errors.NewSDKErrorWithStatus(apiutil.ErrEmptySearchQuery, http.StatusBadRequest),
},
{
desc: "search for users with invalid length of query",
token: validToken,
page: sdk.PageMetadata{
Offset: offset,
Limit: limit,
Username: "a",
},
err: errors.NewSDKErrorWithStatus(apiutil.ErrValidation, http.StatusBadRequest),
},
{
desc: "search for users with invalid limit",
token: validToken,
page: sdk.PageMetadata{
Offset: offset,
Limit: 0,
Username: "user_10",
},
err: errors.NewSDKErrorWithStatus(apiutil.ErrLimitSize, http.StatusBadRequest),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(smqauthn.Session{DomainUserID: validID, UserID: validID, DomainID: domainID}, tc.authenticateErr)
svcCall := svc.On("SearchUsers", mock.Anything, mock.Anything).Return(tc.searchreturn, tc.err)
page, err := mgsdk.SearchUsers(context.Background(), tc.page, tc.token)
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected error %v, got %v", tc.desc, tc.err, err))
assert.Equal(t, tc.response, page.Users, fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.response, page.Users))
svcCall.Unset()
authCall.Unset()
})
}
}
func TestViewUser(t *testing.T) {
ts, svc, auth := setupUsers()
defer ts.Close()
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
cases := []struct {
desc string
token string
session smqauthn.Session
userID string
svcRes users.User
svcErr error
authenticateErr error
response sdk.User
err errors.SDKError
}{
{
desc: "view user successfully",
token: validToken,
userID: user.ID,
svcRes: convertUser(user),
svcErr: nil,
response: user,
err: nil,
},
{
desc: "view user with invalid token",
token: invalidToken,
userID: user.ID,
svcRes: users.User{},
svcErr: svcerr.ErrAuthentication,
authenticateErr: svcerr.ErrAuthentication,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "view user with empty token",
token: "",
userID: user.ID,
svcRes: users.User{},
svcErr: svcerr.ErrAuthentication,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "view user with invalid id",
token: validToken,
userID: wrongID,
svcRes: users.User{},
svcErr: svcerr.ErrNotFound,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound),
},
{
desc: "view user with empty id",
token: validToken,
userID: "",
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(apiutil.ErrMissingID),
},
{
desc: "view user with response that can't be unmarshalled",
token: validToken,
userID: user.ID,
svcRes: users.User{
ID: id,
FirstName: user.FirstName,
LastName: user.LastName,
Metadata: users.Metadata{
"key": make(chan int),
},
},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(fmt.Errorf("unexpected end of JSON input")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("View", mock.Anything, tc.session, tc.userID).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.User(context.Background(), tc.userID, tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "View", mock.Anything, tc.session, tc.userID)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestUserProfile(t *testing.T) {
ts, svc, auth := setupUsers()
defer ts.Close()
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
cases := []struct {
desc string
token string
session smqauthn.Session
svcRes users.User
svcErr error
authenticateErr error
response sdk.User
err errors.SDKError
}{
{
desc: "view user profile successfully",
token: validToken,
svcRes: convertUser(user),
svcErr: nil,
response: user,
err: nil,
},
{
desc: "view user profile with invalid token",
token: invalidToken,
svcRes: users.User{},
svcErr: nil,
authenticateErr: svcerr.ErrAuthentication,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "view user profile with empty token",
token: "",
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "view user profile with response that can't be unmarshalled",
token: validToken,
svcRes: users.User{
ID: id,
FirstName: user.FirstName,
Metadata: users.Metadata{
"key": make(chan int),
},
},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(fmt.Errorf("unexpected end of JSON input")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("ViewProfile", mock.Anything, tc.session).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.UserProfile(context.Background(), tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "ViewProfile", mock.Anything, tc.session)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestUpdateUser(t *testing.T) {
ts, svc, auth := setupUsers()
defer ts.Close()
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
updatedName := "updatedName"
updatedUser := user
updatedUser.FirstName = updatedName
cases := []struct {
desc string
token string
session smqauthn.Session
updateUserReq sdk.User
userID string
svcReq users.UserReq
svcRes users.User
svcErr error
authenticateErr error
response sdk.User
err errors.SDKError
}{
{
desc: "update user name with valid token",
token: validToken,
updateUserReq: sdk.User{
ID: user.ID,
FirstName: updatedName,
},
userID: user.ID,
svcReq: users.UserReq{
FirstName: &updatedName,
},
svcRes: convertUser(updatedUser),
svcErr: nil,
response: updatedUser,
err: nil,
},
{
desc: "update user name with invalid token",
token: invalidToken,
updateUserReq: sdk.User{
ID: user.ID,
FirstName: updatedName,
},
userID: user.ID,
svcReq: users.UserReq{
FirstName: &updatedName,
},
svcRes: users.User{},
authenticateErr: svcerr.ErrAuthentication,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "update user name with invalid id",
token: validToken,
updateUserReq: sdk.User{
ID: wrongID,
FirstName: updatedName,
},
userID: wrongID,
svcReq: users.UserReq{
FirstName: &updatedName,
},
svcRes: users.User{},
svcErr: svcerr.ErrUpdateEntity,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrUpdateEntity, http.StatusUnprocessableEntity),
},
{
desc: "update user name with empty token",
token: "",
updateUserReq: sdk.User{
ID: user.ID,
FirstName: updatedName,
},
userID: user.ID,
svcReq: users.UserReq{
FirstName: &updatedName,
},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "update user name with empty id",
token: validToken,
updateUserReq: sdk.User{
ID: "",
FirstName: updatedName,
},
userID: "",
svcReq: users.UserReq{
FirstName: &updatedName,
},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(apiutil.ErrMissingID),
},
{
desc: "update user with request that can't be marshalled",
token: validToken,
updateUserReq: sdk.User{
ID: generateUUID(t),
Metadata: map[string]any{
"test": make(chan int),
},
},
svcReq: users.UserReq{},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(fmt.Errorf("json: unsupported type: chan int")),
},
{
desc: "update user with response that can't be unmarshalled",
token: validToken,
updateUserReq: sdk.User{
ID: user.ID,
FirstName: updatedName,
},
userID: user.ID,
svcReq: users.UserReq{
FirstName: &updatedName,
},
svcRes: users.User{
ID: id,
FirstName: updatedName,
Metadata: users.Metadata{
"key": make(chan int),
},
},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(fmt.Errorf("unexpected end of JSON input")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("Update", mock.Anything, tc.session, tc.userID, tc.svcReq).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.UpdateUser(context.Background(), 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, "Update", mock.Anything, tc.session, tc.userID, tc.svcReq)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestUpdateUserTags(t *testing.T) {
ts, svc, auth := setupUsers()
defer ts.Close()
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
updatedTags := []string{"updatedTag1", "updatedTag2"}
updatedUser := user
updatedUser.Tags = updatedTags
cases := []struct {
desc string
token string
session smqauthn.Session
updateUserReq sdk.User
userID string
svcReq users.UserReq
svcRes users.User
svcErr error
authenticateErr error
response sdk.User
err errors.SDKError
}{
{
desc: "update user tags with valid token",
token: validToken,
updateUserReq: sdk.User{
ID: user.ID,
Tags: updatedTags,
},
userID: user.ID,
svcReq: users.UserReq{
Tags: &updatedTags,
},
svcRes: convertUser(updatedUser),
svcErr: nil,
response: updatedUser,
err: nil,
},
{
desc: "update user tags with invalid token",
token: invalidToken,
updateUserReq: sdk.User{
ID: user.ID,
Tags: updatedTags,
},
userID: user.ID,
svcReq: users.UserReq{
Tags: &updatedTags,
},
svcRes: users.User{},
authenticateErr: svcerr.ErrAuthentication,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "update user tags with empty token",
token: "",
updateUserReq: sdk.User{
ID: user.ID,
Tags: updatedTags,
},
svcReq: users.UserReq{},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "update user tags with invalid id",
token: validToken,
updateUserReq: sdk.User{
ID: wrongID,
Tags: updatedTags,
},
userID: wrongID,
svcReq: users.UserReq{
Tags: &updatedTags,
},
svcRes: users.User{},
svcErr: svcerr.ErrUpdateEntity,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrUpdateEntity, http.StatusUnprocessableEntity),
},
{
desc: "update user tags with empty id",
token: validToken,
updateUserReq: sdk.User{
ID: "",
Tags: updatedTags,
},
userID: "",
svcReq: users.UserReq{},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingID, http.StatusBadRequest),
},
{
desc: "update user tags with request that can't be marshalled",
token: validToken,
updateUserReq: sdk.User{
ID: generateUUID(t),
Metadata: map[string]any{
"test": make(chan int),
},
},
svcReq: users.UserReq{},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(fmt.Errorf("json: unsupported type: chan int")),
},
{
desc: "update user tags with response that can't be unmarshalled",
token: validToken,
updateUserReq: sdk.User{
ID: user.ID,
Tags: updatedTags,
},
userID: user.ID,
svcReq: users.UserReq{
Tags: &updatedTags,
},
svcRes: users.User{
ID: id,
Tags: updatedTags,
Metadata: users.Metadata{
"key": make(chan int),
},
},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(fmt.Errorf("unexpected end of JSON input")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("UpdateTags", mock.Anything, tc.session, tc.userID, tc.svcReq).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.UpdateUserTags(context.Background(), 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, "UpdateTags", mock.Anything, tc.session, tc.userID, tc.svcReq)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestUpdateUserEmail(t *testing.T) {
ts, svc, auth := setupUsers()
defer ts.Close()
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
updatedEmail := "updatedEmail@email.com"
updatedUser := user
updatedUser.Email = updatedEmail
cases := []struct {
desc string
token string
session smqauthn.Session
updateUserReq sdk.User
svcReq string
svcRes users.User
svcErr error
authenticateErr error
response sdk.User
err errors.SDKError
}{
{
desc: "update email with valid token",
token: validToken,
updateUserReq: sdk.User{
ID: user.ID,
Email: updatedEmail,
Credentials: sdk.Credentials{
Secret: user.Credentials.Secret,
},
},
svcReq: updatedEmail,
svcRes: convertUser(updatedUser),
svcErr: nil,
response: updatedUser,
err: nil,
},
{
desc: "update email with invalid token",
token: invalidToken,
updateUserReq: sdk.User{
ID: user.ID,
Email: updatedEmail,
Credentials: sdk.Credentials{
Secret: user.Credentials.Secret,
},
},
svcReq: updatedEmail,
svcRes: users.User{},
authenticateErr: svcerr.ErrAuthentication,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "update email with empty token",
token: "",
updateUserReq: sdk.User{
ID: user.ID,
Email: updatedEmail,
Credentials: sdk.Credentials{
Secret: user.Credentials.Secret,
},
},
svcReq: updatedEmail,
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "update email with invalid id",
token: validToken,
updateUserReq: sdk.User{
ID: wrongID,
Email: updatedEmail,
Credentials: sdk.Credentials{
Secret: user.Credentials.Secret,
},
},
svcReq: updatedEmail,
svcRes: users.User{},
svcErr: svcerr.ErrUpdateEntity,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrUpdateEntity, http.StatusUnprocessableEntity),
},
{
desc: "update email with empty id",
token: validToken,
updateUserReq: sdk.User{
ID: "",
Email: updatedEmail,
Credentials: sdk.Credentials{
Secret: user.Credentials.Secret,
},
},
svcReq: updatedEmail,
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingID, http.StatusBadRequest),
},
{
desc: "update email with response that can't be unmarshalled",
token: validToken,
updateUserReq: sdk.User{
ID: user.ID,
Email: updatedEmail,
Credentials: sdk.Credentials{
Secret: user.Credentials.Secret,
},
},
svcReq: updatedEmail,
svcRes: users.User{
ID: id,
FirstName: updatedEmail,
Metadata: users.Metadata{
"key": make(chan int),
},
},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(fmt.Errorf("unexpected end of JSON input")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("UpdateEmail", mock.Anything, tc.session, tc.updateUserReq.ID, tc.svcReq).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.UpdateUserEmail(context.Background(), 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, "UpdateEmail", mock.Anything, tc.session, tc.updateUserReq.ID, tc.svcReq)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestResetPasswordRequest(t *testing.T) {
ts, svc, _ := setupUsers()
defer ts.Close()
defHost := "http://localhost"
conf := sdk.Config{
UsersURL: ts.URL,
HostURL: defHost,
}
mgsdk := sdk.NewSDK(conf)
validEmail := "test@email.com"
cases := []struct {
desc string
email string
svcRes users.User
svcErr error
issueRes *grpcTokenV1.Token
issueErr error
err errors.SDKError
}{
{
desc: "reset password request with valid email",
email: validEmail,
svcRes: convertUser(user),
svcErr: nil,
issueRes: &grpcTokenV1.Token{AccessToken: validToken, RefreshToken: &validToken},
err: nil,
},
{
desc: "reset password request with invalid email",
email: "invalidemail",
svcRes: users.User{},
svcErr: svcerr.ErrNotFound,
issueRes: &grpcTokenV1.Token{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound),
},
{
desc: "reset password request with empty email",
email: "",
svcRes: users.User{},
svcErr: nil,
issueRes: &grpcTokenV1.Token{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingEmail, http.StatusBadRequest),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
svcCall := svc.On("SendPasswordReset", mock.Anything, tc.email).Return(tc.svcErr)
err := mgsdk.ResetPasswordRequest(context.Background(), tc.email)
assert.Equal(t, tc.err, err)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "SendPasswordReset", mock.Anything, tc.email)
assert.True(t, ok)
}
svcCall.Unset()
})
}
}
func TestResetPassword(t *testing.T) {
ts, svc, auth := setupUsers()
defer ts.Close()
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
newPassword := "newPassword"
cases := []struct {
desc string
token string
session smqauthn.Session
newPassword string
confPassword string
svcErr error
authenticateErr error
err errors.SDKError
}{
{
desc: "reset password successfully",
token: validToken,
session: smqauthn.Session{UserID: validID, DomainID: domainID},
newPassword: newPassword,
confPassword: newPassword,
svcErr: nil,
err: nil,
},
{
desc: "reset password with invalid token",
token: invalidToken,
newPassword: newPassword,
confPassword: newPassword,
authenticateErr: svcerr.ErrAuthentication,
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "reset password with empty token",
token: "",
newPassword: newPassword,
confPassword: newPassword,
svcErr: nil,
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "reset password with empty new password",
token: validToken,
session: smqauthn.Session{UserID: validID, DomainID: domainID},
newPassword: "",
confPassword: newPassword,
svcErr: nil,
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingPass, http.StatusBadRequest),
},
{
desc: "reset password with empty confirm password",
token: validToken,
session: smqauthn.Session{UserID: validID, DomainID: domainID},
newPassword: newPassword,
confPassword: "",
svcErr: nil,
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingConfPass, http.StatusBadRequest),
},
{
desc: "reset password with new password not matching confirm password",
token: validToken,
session: smqauthn.Session{UserID: validID, DomainID: domainID},
newPassword: newPassword,
confPassword: "wrongPassword",
svcErr: nil,
err: errors.NewSDKErrorWithStatus(apiutil.ErrInvalidResetPass, http.StatusBadRequest),
},
{
desc: "reset password with weak password",
token: validToken,
session: smqauthn.Session{UserID: validID, DomainID: domainID},
newPassword: "weak",
confPassword: "weak",
svcErr: nil,
err: errors.NewSDKErrorWithStatus(apiutil.ErrPasswordFormat, http.StatusBadRequest),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("ResetSecret", mock.Anything, tc.session, tc.newPassword).Return(tc.svcErr)
err := mgsdk.ResetPassword(context.Background(), tc.newPassword, tc.confPassword, tc.token)
assert.Equal(t, tc.err, err)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "ResetSecret", mock.Anything, tc.session, tc.newPassword)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestUpdatePassword(t *testing.T) {
ts, svc, auth := setupUsers()
defer ts.Close()
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
newPassword := "newPassword"
updatedUser := user
updatedUser.Credentials.Secret = newPassword
cases := []struct {
desc string
token string
session smqauthn.Session
oldPassword string
newPassword string
svcRes users.User
svcErr error
authenticateErr error
response sdk.User
err errors.SDKError
}{
{
desc: "update password successfully",
token: validToken,
oldPassword: secret,
newPassword: newPassword,
svcRes: convertUser(updatedUser),
svcErr: nil,
response: updatedUser,
err: nil,
},
{
desc: "update password with invalid token",
token: invalidToken,
oldPassword: secret,
newPassword: newPassword,
svcRes: users.User{},
authenticateErr: svcerr.ErrAuthentication,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "update password with empty token",
token: "",
oldPassword: secret,
newPassword: newPassword,
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "update password with empty old password",
token: validToken,
oldPassword: "",
newPassword: newPassword,
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingPass, http.StatusBadRequest),
},
{
desc: "update password with empty new password",
token: validToken,
oldPassword: secret,
newPassword: "",
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingPass, http.StatusBadRequest),
},
{
desc: "update password with invalid new password",
token: validToken,
oldPassword: secret,
newPassword: "weak",
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrPasswordFormat, http.StatusBadRequest),
},
{
desc: "update password with invalid old password",
token: validToken,
oldPassword: "wrongPassword",
newPassword: newPassword,
svcRes: users.User{},
svcErr: svcerr.ErrLogin,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrLogin, http.StatusUnauthorized),
},
{
desc: "update password with response that can't be unmarshalled",
token: validToken,
oldPassword: secret,
newPassword: newPassword,
svcRes: users.User{
ID: id,
FirstName: user.FirstName,
Metadata: users.Metadata{
"key": make(chan int),
},
},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(fmt.Errorf("unexpected end of JSON input")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("UpdateSecret", mock.Anything, tc.session, tc.oldPassword, tc.newPassword).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.UpdatePassword(context.Background(), tc.oldPassword, tc.newPassword, tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "UpdateSecret", mock.Anything, tc.session, tc.oldPassword, tc.newPassword)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestUpdateUserRole(t *testing.T) {
ts, svc, auth := setupUsers()
defer ts.Close()
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
updatedUser := user
updatedRole := users.AdminRole.String()
updatedUser.Role = updatedRole
cases := []struct {
desc string
token string
session smqauthn.Session
updateUserReq sdk.User
svcReq users.User
svcRes users.User
svcErr error
authenticateErr error
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 invalid token",
token: invalidToken,
updateUserReq: sdk.User{
ID: user.ID,
Role: updatedRole,
},
svcReq: users.User{
ID: user.ID,
Role: users.AdminRole,
},
svcRes: users.User{},
authenticateErr: svcerr.ErrAuthentication,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "update user role with empty token",
token: "",
updateUserReq: sdk.User{
ID: user.ID,
Role: updatedRole,
},
svcReq: users.User{},
svcRes: users.User{},
svcErr: nil,
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 empty id",
token: validToken,
updateUserReq: sdk.User{
ID: "",
Role: updatedRole,
},
svcReq: users.User{},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingID, http.StatusBadRequest),
},
{
desc: "update user role with request that can't be marshalled",
token: validToken,
updateUserReq: sdk.User{
ID: generateUUID(t),
Metadata: map[string]any{
"test": make(chan int),
},
},
svcReq: users.User{},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(fmt.Errorf("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(fmt.Errorf("unexpected end of JSON input")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.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.svcReq).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.UpdateUserRole(context.Background(), 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.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 smqauthn.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(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(fmt.Errorf("unexpected end of JSON input")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.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(context.Background(), 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 smqauthn.Session
updateUserReq sdk.User
userID string
svcReq users.UserReq
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,
},
userID: user.ID,
svcReq: users.UserReq{
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,
},
userID: user.ID,
svcReq: users.UserReq{
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,
},
userID: user.ID,
svcReq: users.UserReq{
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,
},
userID: wrongID,
svcReq: users.UserReq{
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,
},
userID: "",
svcReq: users.UserReq{
ProfilePicture: &updatedProfilePicture,
},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(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]any{
"test": make(chan int),
},
},
svcReq: users.UserReq{},
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(fmt.Errorf("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,
},
userID: user.ID,
svcReq: users.UserReq{
ProfilePicture: &updatedProfilePicture,
},
svcRes: users.User{
ID: id,
Metadata: users.Metadata{
"key": make(chan int),
},
},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(fmt.Errorf("unexpected end of JSON input")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.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.userID, tc.svcReq).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.UpdateProfilePicture(context.Background(), 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.userID, tc.svcReq)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestEnableUser(t *testing.T) {
ts, svc, auth := setupUsers()
defer ts.Close()
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
enabledUser := user
enabledUser.Status = users.EnabledStatus.String()
cases := []struct {
desc string
token string
session smqauthn.Session
userID string
svcRes users.User
svcErr error
authenticateErr error
response sdk.User
err errors.SDKError
}{
{
desc: "enable user with valid token",
token: validToken,
userID: user.ID,
svcRes: convertUser(enabledUser),
svcErr: nil,
response: enabledUser,
err: nil,
},
{
desc: "enable user with invalid token",
token: invalidToken,
userID: user.ID,
svcRes: users.User{},
authenticateErr: svcerr.ErrAuthentication,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "enable user with empty token",
token: "",
userID: user.ID,
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("Enable", mock.Anything, tc.session, tc.userID).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.EnableUser(context.Background(), tc.userID, tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "Enable", mock.Anything, tc.session, tc.userID)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestDisableUser(t *testing.T) {
ts, svc, auth := setupUsers()
defer ts.Close()
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
disabledUser := user
disabledUser.Status = users.DisabledStatus.String()
cases := []struct {
desc string
token string
session smqauthn.Session
userID string
svcRes users.User
svcErr error
authenticateErr error
response sdk.User
err errors.SDKError
}{
{
desc: "disable user with valid token",
token: validToken,
userID: user.ID,
svcRes: convertUser(disabledUser),
svcErr: nil,
response: disabledUser,
err: nil,
},
{
desc: "disable user with invalid token",
token: invalidToken,
userID: user.ID,
svcRes: users.User{},
authenticateErr: svcerr.ErrAuthentication,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "disable user with empty token",
token: "",
userID: user.ID,
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "disable user with invalid id",
token: validToken,
userID: wrongID,
svcRes: users.User{},
svcErr: svcerr.ErrUpdateEntity,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrUpdateEntity, http.StatusUnprocessableEntity),
},
{
desc: "disable user with empty id",
token: validToken,
userID: "",
svcRes: users.User{},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingID, http.StatusBadRequest),
},
{
desc: "disable user with response that can't be unmarshalled",
token: validToken,
userID: user.ID,
svcRes: users.User{
ID: id,
Status: users.DisabledStatus,
Metadata: users.Metadata{
"key": make(chan int),
},
},
svcErr: nil,
response: sdk.User{},
err: errors.NewSDKError(fmt.Errorf("unexpected end of JSON input")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("Disable", mock.Anything, tc.session, tc.userID).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.DisableUser(context.Background(), tc.userID, tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "Disable", mock.Anything, tc.session, tc.userID)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestDeleteUser(t *testing.T) {
ts, svc, auth := setupUsers()
defer ts.Close()
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
cases := []struct {
desc string
token string
session smqauthn.Session
userID string
svcErr error
authenticateErr error
err errors.SDKError
}{
{
desc: "delete user successfully",
token: validToken,
userID: validID,
svcErr: nil,
err: nil,
},
{
desc: "delete user with invalid token",
token: invalidToken,
userID: validID,
authenticateErr: svcerr.ErrAuthentication,
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "delete user with empty token",
token: "",
userID: validID,
svcErr: nil,
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "delete user with invalid id",
token: validToken,
userID: wrongID,
svcErr: svcerr.ErrRemoveEntity,
err: errors.NewSDKErrorWithStatus(svcerr.ErrRemoveEntity, http.StatusUnprocessableEntity),
},
{
desc: "delete user with empty id",
token: validToken,
userID: "",
svcErr: nil,
err: errors.NewSDKError(apiutil.ErrMissingID),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("Delete", mock.Anything, tc.session, tc.userID).Return(tc.svcErr)
err := mgsdk.DeleteUser(context.Background(), tc.userID, tc.token)
assert.Equal(t, tc.err, err)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "Delete", mock.Anything, tc.session, tc.userID)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}