diff --git a/pkg/sdk/go/channels.go b/pkg/sdk/go/channels.go index a607ce7cbb..85fa4f2bb4 100644 --- a/pkg/sdk/go/channels.go +++ b/pkg/sdk/go/channels.go @@ -9,6 +9,7 @@ import ( "net/http" "time" + "github.com/absmach/magistrala/internal/apiutil" "github.com/absmach/magistrala/pkg/errors" ) @@ -90,6 +91,9 @@ func (sdk mgSDK) ChannelsByThing(thingID string, pm PageMetadata, token string) } func (sdk mgSDK) Channel(id, token string) (Channel, errors.SDKError) { + if id == "" { + return Channel{}, errors.NewSDKError(apiutil.ErrMissingID) + } url := fmt.Sprintf("%s/%s/%s", sdk.thingsURL, channelsEndpoint, id) _, body, err := sdk.processRequest(http.MethodGet, url, token, nil, nil, http.StatusOK) @@ -127,6 +131,9 @@ func (sdk mgSDK) UpdateChannel(c Channel, token string) (Channel, errors.SDKErro return Channel{}, errors.NewSDKError(err) } + if c.ID == "" { + return Channel{}, errors.NewSDKError(apiutil.ErrMissingID) + } url := fmt.Sprintf("%s/%s/%s", sdk.thingsURL, channelsEndpoint, c.ID) _, body, sdkerr := sdk.processRequest(http.MethodPut, url, token, data, nil, http.StatusOK) @@ -275,6 +282,9 @@ func (sdk mgSDK) DisableChannel(id, token string) (Channel, errors.SDKError) { } func (sdk mgSDK) DeleteChannel(id, token string) errors.SDKError { + if id == "" { + return errors.NewSDKError(apiutil.ErrMissingID) + } url := fmt.Sprintf("%s/%s/%s", sdk.thingsURL, channelsEndpoint, id) _, _, sdkerr := sdk.processRequest(http.MethodDelete, url, token, nil, nil, http.StatusNoContent) return sdkerr diff --git a/pkg/sdk/go/channels_test.go b/pkg/sdk/go/channels_test.go index 431c65a43d..623d6e2d0b 100644 --- a/pkg/sdk/go/channels_test.go +++ b/pkg/sdk/go/channels_test.go @@ -8,9 +8,11 @@ import ( "net/http" "net/http/httptest" "reflect" + "strings" "testing" "time" + "github.com/absmach/magistrala/auth" "github.com/absmach/magistrala/internal/apiutil" "github.com/absmach/magistrala/internal/testsutil" mglog "github.com/absmach/magistrala/logger" @@ -19,21 +21,37 @@ import ( svcerr "github.com/absmach/magistrala/pkg/errors/service" "github.com/absmach/magistrala/pkg/groups" gmocks "github.com/absmach/magistrala/pkg/groups/mocks" + oauth2mocks "github.com/absmach/magistrala/pkg/oauth2/mocks" sdk "github.com/absmach/magistrala/pkg/sdk/go" - api "github.com/absmach/magistrala/things/api/http" + thapi "github.com/absmach/magistrala/things/api/http" thmocks "github.com/absmach/magistrala/things/mocks" + usapi "github.com/absmach/magistrala/users/api" + usmocks "github.com/absmach/magistrala/users/mocks" "github.com/go-chi/chi/v5" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) +var ( + channelName = "channelName" + newName = "newName" + newDescription = "newDescription" + channel = generateTestChannel(&testing.T{}) + group = convertChannel(channel) +) + func setupChannels() (*httptest.Server, *gmocks.Service) { tsvc := new(thmocks.Service) + usvc := new(usmocks.Service) gsvc := new(gmocks.Service) logger := mglog.NewMock() + provider := new(oauth2mocks.Provider) + provider.On("Name").Return("test") + mux := chi.NewRouter() - api.MakeHandler(tsvc, gsvc, mux, logger, "") + thapi.MakeHandler(tsvc, gsvc, mux, logger, "") + usapi.MakeHandler(usvc, gsvc, mux, logger, "", passRegex, provider) return httptest.NewServer(mux), gsvc } @@ -41,37 +59,24 @@ func TestCreateChannel(t *testing.T) { ts, gsvc := setupChannels() defer ts.Close() - channel := sdk.Channel{ - Name: "channelName", - Metadata: validMetadata, - Status: mgclients.EnabledStatus.String(), - } - - groupMeta := groups.Group{ + createGroupReq := groups.Group{ Name: channel.Name, Metadata: mgclients.Metadata{"role": "client"}, Status: mgclients.EnabledStatus, } - createdAt, err := time.Parse(time.RFC3339, "2023-03-03T00:00:00Z") // fix - assert.Nil(t, err, fmt.Sprintf("unexpected error %s", err)) - updatedAt := createdAt - group := groups.Group{ - ID: testsutil.GenerateUUID(&testing.T{}), - Domain: testsutil.GenerateUUID(&testing.T{}), - Name: channel.Name, - Description: channel.Description, - Metadata: mgclients.Metadata{"role": "client"}, - CreatedAt: createdAt, - UpdatedAt: updatedAt, - UpdatedBy: testsutil.GenerateUUID(&testing.T{}), - Status: mgclients.EnabledStatus, + channelReq := sdk.Channel{ + Name: channel.Name, + Metadata: validMetadata, + Status: mgclients.EnabledStatus.String(), } channelKind := "new_channel" parentID := testsutil.GenerateUUID(&testing.T{}) pGroup := group pGroup.Parent = parentID + pChannel := channel + pChannel.ParentID = parentID iGroup := group iGroup.Metadata = mgclients.Metadata{ @@ -83,138 +88,149 @@ func TestCreateChannel(t *testing.T) { } mgsdk := sdk.NewSDK(conf) cases := []struct { - desc string - channel sdk.Channel - token string - groupMeta groups.Group - svcRes groups.Group - svcErr error - err errors.SDKError + desc string + channelReq sdk.Channel + token string + createGroupReq groups.Group + svcRes groups.Group + svcErr error + response sdk.Channel + err errors.SDKError }{ { - desc: "create channel successfully", - channel: channel, - token: validToken, - groupMeta: groupMeta, - svcRes: group, - svcErr: nil, - err: nil, + desc: "create channel successfully", + channelReq: channelReq, + token: validToken, + createGroupReq: createGroupReq, + svcRes: group, + svcErr: nil, + response: channel, + err: nil, }, { - desc: "create channel with existing name", - channel: channel, - token: validToken, - groupMeta: groupMeta, - svcRes: group, - svcErr: nil, - err: nil, + desc: "create channel with existing name", + channelReq: channelReq, + token: validToken, + createGroupReq: createGroupReq, + svcRes: groups.Group{}, + svcErr: svcerr.ErrCreateEntity, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrCreateEntity, http.StatusUnprocessableEntity), }, { - desc: "update channel that can't be marshalled", - channel: sdk.Channel{ + desc: "create channel that can't be marshalled", + channelReq: sdk.Channel{ Name: "test", Metadata: map[string]interface{}{ "test": make(chan int), }, }, - token: validToken, - groupMeta: groups.Group{}, - svcRes: groups.Group{}, - svcErr: nil, - err: errors.NewSDKError(fmt.Errorf("json: unsupported type: chan int")), + token: validToken, + createGroupReq: groups.Group{}, + svcRes: groups.Group{}, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKError(errors.New("json: unsupported type: chan int")), }, { desc: "create channel with parent", - channel: sdk.Channel{ + channelReq: sdk.Channel{ Name: channel.Name, ParentID: parentID, Status: mgclients.EnabledStatus.String(), }, token: validToken, - groupMeta: groups.Group{ + createGroupReq: groups.Group{ Name: channel.Name, Parent: parentID, Status: mgclients.EnabledStatus, }, - svcRes: pGroup, - svcErr: nil, - err: nil, + svcRes: pGroup, + svcErr: nil, + response: pChannel, + err: nil, }, { desc: "create channel with invalid parent", - channel: sdk.Channel{ + channelReq: sdk.Channel{ Name: channel.Name, ParentID: wrongID, Status: mgclients.EnabledStatus.String(), }, token: validToken, - groupMeta: groups.Group{ + createGroupReq: groups.Group{ Name: channel.Name, Parent: wrongID, Status: mgclients.EnabledStatus, }, - svcRes: groups.Group{}, - svcErr: svcerr.ErrCreateEntity, - err: errors.NewSDKErrorWithStatus(svcerr.ErrCreateEntity, http.StatusUnprocessableEntity), + svcRes: groups.Group{}, + svcErr: svcerr.ErrCreateEntity, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrCreateEntity, http.StatusUnprocessableEntity), }, { desc: "create channel with missing name", - channel: sdk.Channel{ + channelReq: sdk.Channel{ Status: mgclients.EnabledStatus.String(), }, - token: validToken, - groupMeta: groups.Group{}, - svcRes: groups.Group{}, - svcErr: nil, - err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrNameSize), http.StatusBadRequest), + token: validToken, + createGroupReq: groups.Group{}, + svcRes: groups.Group{}, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrNameSize), http.StatusBadRequest), }, { desc: "create a channel with every field defined", - channel: sdk.Channel{ + channelReq: sdk.Channel{ ID: group.ID, ParentID: parentID, Name: channel.Name, Description: description, Metadata: validMetadata, - CreatedAt: createdAt, - UpdatedAt: updatedAt, + CreatedAt: group.CreatedAt, + UpdatedAt: group.UpdatedAt, Status: mgclients.EnabledStatus.String(), }, token: validToken, - groupMeta: groups.Group{ + createGroupReq: groups.Group{ ID: group.ID, Parent: parentID, Name: channel.Name, Description: description, Metadata: mgclients.Metadata{"role": "client"}, - CreatedAt: createdAt, - UpdatedAt: updatedAt, + CreatedAt: group.CreatedAt, + UpdatedAt: group.UpdatedAt, Status: mgclients.EnabledStatus, }, - svcRes: group, - svcErr: nil, - err: nil, + svcRes: pGroup, + svcErr: nil, + response: pChannel, + err: nil, }, { - desc: "create channel with response that can't be unmarshalled", - channel: channel, - token: validToken, - groupMeta: groupMeta, - svcRes: iGroup, - svcErr: nil, - err: errors.NewSDKError(fmt.Errorf("unexpected end of JSON input")), + desc: "create channel with response that can't be unmarshalled", + channelReq: channelReq, + token: validToken, + createGroupReq: createGroupReq, + svcRes: iGroup, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKError(errors.New("unexpected end of JSON input")), }, } for _, tc := range cases { - svcCall := gsvc.On("CreateGroup", mock.Anything, tc.token, channelKind, tc.groupMeta).Return(tc.svcRes, tc.svcErr) - rChannel, err := mgsdk.CreateChannel(tc.channel, validToken) - assert.Equal(t, tc.err, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err)) - if tc.err == nil { - ok := svcCall.Parent.AssertCalled(t, "CreateGroup", mock.Anything, tc.token, channelKind, tc.groupMeta) - assert.True(t, ok, fmt.Sprintf("%s: CreateGroup was not called", tc.desc)) - assert.NotEmpty(t, rChannel, fmt.Sprintf("%s: expected not nil on client ID", tc.desc)) // fix - } - svcCall.Unset() + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("CreateGroup", mock.Anything, tc.token, channelKind, tc.createGroupReq).Return(tc.svcRes, tc.svcErr) + resp, err := mgsdk.CreateChannel(tc.channelReq, validToken) + assert.Equal(t, tc.err, err) + assert.Equal(t, tc.response, resp) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "CreateGroup", mock.Anything, tc.token, channelKind, tc.createGroupReq) + assert.True(t, ok) + } + svcCall.Unset() + }) } } @@ -238,8 +254,6 @@ func TestListChannels(t *testing.T) { chs = append(chs, gr) } - memberKind := "users" - cases := []struct { desc string token string @@ -253,11 +267,11 @@ func TestListChannels(t *testing.T) { groupsPageMeta groups.Page svcRes groups.Page svcErr error + response sdk.ChannelsPage err errors.SDKError - response []sdk.Channel }{ { - desc: "get a list of channels", + desc: "list channels successfully", token: validToken, limit: limit, offset: offset, @@ -270,12 +284,22 @@ func TestListChannels(t *testing.T) { Permission: "view", Direction: -1, }, - svcRes: groups.Page{Groups: convertChannels(chs[offset:limit])}, - err: nil, - response: chs[offset:limit], + svcRes: groups.Page{ + PageMeta: groups.PageMeta{ + Total: uint64(len(chs[offset:limit])), + }, + Groups: convertChannels(chs[offset:limit]), + }, + response: sdk.ChannelsPage{ + PageRes: sdk.PageRes{ + Total: uint64(len(chs[offset:limit])), + }, + Channels: chs[offset:limit], + }, + err: nil, }, { - desc: "get a list of channels with invalid token", + desc: "list channels with invalid token", token: invalidToken, offset: offset, limit: limit, @@ -289,22 +313,22 @@ func TestListChannels(t *testing.T) { }, svcRes: groups.Page{}, svcErr: svcerr.ErrAuthentication, + response: sdk.ChannelsPage{}, err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), - response: nil, }, { - desc: "get a list of channels with empty token", + desc: "list channels with empty token", token: "", offset: offset, limit: limit, groupsPageMeta: groups.Page{}, svcRes: groups.Page{}, svcErr: nil, + response: sdk.ChannelsPage{}, err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized), - response: nil, }, { - desc: "get a list of channels with zero limit", + desc: "list channels with zero limit", token: token, offset: offset, limit: 0, @@ -316,575 +340,2104 @@ func TestListChannels(t *testing.T) { Permission: "view", Direction: -1, }, - svcRes: groups.Page{Groups: convertChannels(chs[offset:limit])}, - svcErr: nil, - err: nil, - response: chs[offset:limit], + svcRes: groups.Page{ + PageMeta: groups.PageMeta{ + Total: uint64(len(chs[offset:])), + }, + Groups: convertChannels(chs[offset:limit]), + }, + svcErr: nil, + response: sdk.ChannelsPage{ + PageRes: sdk.PageRes{ + Total: uint64(len(chs[offset:])), + }, + Channels: chs[offset:limit], + }, + err: nil, }, { - desc: "get a list of channels with limit greater than max", + desc: "list channels with limit greater than max", token: token, offset: offset, limit: 110, groupsPageMeta: groups.Page{}, svcRes: groups.Page{}, svcErr: nil, + response: sdk.ChannelsPage{}, err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrLimitSize), http.StatusBadRequest), - response: []sdk.Channel(nil), - }, - // { - // desc: "get a list of channels with given name", - // token: token, - // offset: 0, - // limit: 1, - // err: nil, - // metadata: sdk.Metadata{}, - // response: []sdk.Channel{chs[89]}, - // }, - // { - // desc: "get a list of channels with level", - // token: token, - // offset: 0, - // limit: 1, - // level: 1, - // err: nil, - // response: []sdk.Channel{chs[0]}, - // }, - // { - // desc: "get a list of channels with metadata", - // token: token, - // offset: 0, - // limit: 1, - // err: nil, - // metadata: sdk.Metadata{}, - // response: []sdk.Channel{chs[89]}, - // }, + }, + { + desc: "list channels with level", + token: token, + offset: 0, + limit: 1, + level: 1, + groupsPageMeta: groups.Page{ + PageMeta: groups.PageMeta{ + Offset: offset, + Limit: 1, + }, + Level: 1, + Permission: "view", + Direction: -1, + }, + svcRes: groups.Page{ + PageMeta: groups.PageMeta{ + Total: 1, + }, + Groups: convertChannels(chs[0:1]), + }, + svcErr: nil, + response: sdk.ChannelsPage{ + PageRes: sdk.PageRes{ + Total: 1, + }, + Channels: chs[0:1], + }, + err: nil, + }, + { + desc: "list channels with metadata", + token: token, + offset: 0, + limit: 10, + metadata: sdk.Metadata{"name": "thing_89"}, + groupsPageMeta: groups.Page{ + PageMeta: groups.PageMeta{ + Offset: offset, + Limit: 10, + Metadata: mgclients.Metadata{"name": "thing_89"}, + }, + Permission: "view", + Direction: -1, + }, + svcRes: groups.Page{ + PageMeta: groups.PageMeta{ + Total: 1, + }, + Groups: convertChannels([]sdk.Channel{chs[89]}), + }, + svcErr: nil, + response: sdk.ChannelsPage{ + PageRes: sdk.PageRes{ + Total: 1, + }, + Channels: []sdk.Channel{chs[89]}, + }, + err: nil, + }, + { + desc: "list channels with invalid metadata", + token: token, + offset: 0, + limit: 10, + metadata: sdk.Metadata{ + "test": make(chan int), + }, + groupsPageMeta: groups.Page{}, + svcRes: groups.Page{}, + svcErr: nil, + response: sdk.ChannelsPage{}, + err: errors.NewSDKError(errors.New("json: unsupported type: chan int")), + }, + { + desc: "list channels with service response that can't be unmarshalled", + token: token, + offset: 0, + limit: 10, + groupsPageMeta: groups.Page{ + PageMeta: groups.PageMeta{ + Offset: 0, + Limit: 10, + }, + Permission: "view", + Direction: -1, + }, + svcRes: groups.Page{ + PageMeta: groups.PageMeta{ + Total: 1, + }, + Groups: []groups.Group{{ + ID: generateUUID(t), + Metadata: mgclients.Metadata{ + "test": make(chan int), + }, + }}, + }, + svcErr: nil, + response: sdk.ChannelsPage{}, + err: errors.NewSDKError(errors.New("unexpected end of JSON input")), + }, } for _, tc := range cases { - pm := sdk.PageMetadata{ - Offset: tc.offset, - Limit: tc.limit, - Level: uint64(tc.level), - } - svcCall := gsvc.On("ListGroups", mock.Anything, tc.token, memberKind, "", tc.groupsPageMeta).Return(tc.svcRes, tc.svcErr) - page, err := mgsdk.Channels(pm, tc.token) - assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected error %s, got %s", tc.desc, tc.err, err)) - assert.Equal(t, len(tc.response), len(page.Channels), fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.response, page)) - if tc.err == nil { - ok := svcCall.Parent.AssertCalled(t, "ListGroups", mock.Anything, tc.token, memberKind, "", tc.groupsPageMeta) - assert.True(t, ok, fmt.Sprintf("ListGroups was not called on %s", tc.desc)) - } - svcCall.Unset() + t.Run(tc.desc, func(t *testing.T) { + pm := sdk.PageMetadata{ + Offset: tc.offset, + Limit: tc.limit, + Level: uint64(tc.level), + Metadata: tc.metadata, + } + svcCall := gsvc.On("ListGroups", mock.Anything, tc.token, auth.UsersKind, "", tc.groupsPageMeta).Return(tc.svcRes, tc.svcErr) + resp, err := mgsdk.Channels(pm, tc.token) + assert.Equal(t, tc.err, err) + assert.Equal(t, tc.response, resp) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "ListGroups", mock.Anything, tc.token, auth.UsersKind, "", tc.groupsPageMeta) + assert.True(t, ok) + } + svcCall.Unset() + }) } } -// func TestViewChannel(t *testing.T) { -// ts, grepo, auth := setupChannels() -// defer ts.Close() - -// channel := sdk.Channel{ -// Name: "channelName", -// Description: description, -// Metadata: validMetadata, -// Children: []*sdk.Channel{}, -// Status: mgclients.EnabledStatus.String(), -// } - -// conf := sdk.Config{ -// ThingsURL: ts.URL, -// } -// mgsdk := sdk.NewSDK(conf) -// channel.ID = generateUUID(t) - -// cases := []struct { -// desc string -// token string -// channelID string -// response sdk.Channel -// err errors.SDKError -// }{ -// { -// desc: "view channel", -// token: validToken, -// channelID: channel.ID, -// response: channel, -// err: nil, -// }, -// { -// desc: "view channel with invalid token", -// token: "wrongtoken", -// channelID: channel.ID, -// response: sdk.Channel{Children: []*sdk.Channel{}}, -// err: errors.NewSDKErrorWithStatus(svcerr.ErrViewEntity, http.StatusBadRequest), -// }, -// { -// desc: "view channel for wrong id", -// token: validToken, -// channelID: wrongID, -// response: sdk.Channel{Children: []*sdk.Channel{}}, -// err: errors.NewSDKErrorWithStatus(svcerr.ErrViewEntity, http.StatusBadRequest), -// }, -// } - -// for _, tc := range cases { -// repoCall := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) -// repoCall1 := grepo.On("RetrieveByID", mock.Anything, tc.channelID).Return(convertChannel(tc.response), tc.err) -// grp, err := mgsdk.Channel(tc.channelID, tc.token) -// assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected error %s, got %s", tc.desc, tc.err, err)) -// if len(tc.response.Children) == 0 { -// tc.response.Children = nil -// } -// if len(grp.Children) == 0 { -// grp.Children = nil -// } -// assert.Equal(t, tc.response, grp, fmt.Sprintf("%s: expected metadata %v got %v\n", tc.desc, tc.response, grp)) -// if tc.err == nil { -// ok := repoCall1.Parent.AssertCalled(t, "RetrieveByID", mock.Anything, tc.channelID) -// assert.True(t, ok, fmt.Sprintf("RetrieveByID was not called on %s", tc.desc)) -// } -// repoCall.Unset() -// repoCall1.Unset() -// } -// } - -// func TestUpdateChannel(t *testing.T) { -// ts, grepo, auth := setupChannels() -// defer ts.Close() - -// channel := sdk.Channel{ -// ID: generateUUID(t), -// Name: "channelsName", -// Description: description, -// Metadata: validMetadata, -// } - -// conf := sdk.Config{ -// ThingsURL: ts.URL, -// } -// mgsdk := sdk.NewSDK(conf) - -// channel.ID = generateUUID(t) - -// cases := []struct { -// desc string -// token string -// channel sdk.Channel -// response sdk.Channel -// err errors.SDKError -// }{ -// { -// desc: "update channel name", -// channel: sdk.Channel{ -// ID: channel.ID, -// Name: "NewName", -// }, -// response: sdk.Channel{ -// ID: channel.ID, -// Name: "NewName", -// }, -// token: validToken, -// err: nil, -// }, -// { -// desc: "update channel description", -// channel: sdk.Channel{ -// ID: channel.ID, -// Description: "NewDescription", -// }, -// response: sdk.Channel{ -// ID: channel.ID, -// Description: "NewDescription", -// }, -// token: validToken, -// err: nil, -// }, -// { -// desc: "update channel metadata", -// channel: sdk.Channel{ -// ID: channel.ID, -// Metadata: sdk.Metadata{ -// "field": "value2", -// }, -// }, -// response: sdk.Channel{ -// ID: channel.ID, -// Metadata: sdk.Metadata{ -// "field": "value2", -// }, -// }, -// token: validToken, -// err: nil, -// }, -// { -// desc: "update channel name with invalid channel id", -// channel: sdk.Channel{ -// ID: wrongID, -// Name: "NewName", -// }, -// response: sdk.Channel{}, -// token: validToken, -// err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound), -// }, -// { -// desc: "update channel description with invalid channel id", -// channel: sdk.Channel{ -// ID: wrongID, -// Description: "NewDescription", -// }, -// response: sdk.Channel{}, -// token: validToken, -// err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound), -// }, -// { -// desc: "update channel metadata with invalid channel id", -// channel: sdk.Channel{ -// ID: wrongID, -// Metadata: sdk.Metadata{ -// "field": "value2", -// }, -// }, -// response: sdk.Channel{}, -// token: validToken, -// err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound), -// }, -// { -// desc: "update channel name with invalid token", -// channel: sdk.Channel{ -// ID: channel.ID, -// Name: "NewName", -// }, -// response: sdk.Channel{}, -// token: invalidToken, -// err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), -// }, -// { -// desc: "update channel description with invalid token", -// channel: sdk.Channel{ -// ID: channel.ID, -// Description: "NewDescription", -// }, -// response: sdk.Channel{}, -// token: invalidToken, -// err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), -// }, -// { -// desc: "update channel metadata with invalid token", -// channel: sdk.Channel{ -// ID: channel.ID, -// Metadata: sdk.Metadata{ -// "field": "value2", -// }, -// }, -// response: sdk.Channel{}, -// token: invalidToken, -// err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), -// }, -// { -// desc: "update channel that can't be marshalled", -// channel: sdk.Channel{ -// Name: "test", -// Metadata: map[string]interface{}{ -// "test": make(chan int), -// }, -// }, -// response: sdk.Channel{}, -// token: token, -// err: errors.NewSDKError(fmt.Errorf("json: unsupported type: chan int")), -// }, -// } - -// for _, tc := range cases { -// repoCall := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) -// repoCall1 := grepo.On("Update", mock.Anything, mock.Anything).Return(convertChannel(tc.response), tc.err) -// _, err := mgsdk.UpdateChannel(tc.channel, tc.token) -// assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected error %s, got %s", tc.desc, tc.err, err)) -// if tc.err == nil { -// ok := repoCall1.Parent.AssertCalled(t, "Update", mock.Anything, mock.Anything) -// assert.True(t, ok, fmt.Sprintf("Update was not called on %s", tc.desc)) -// } -// repoCall.Unset() -// repoCall1.Unset() -// } -// } - -// func TestListChannelsByThing(t *testing.T) { -// ts, grepo, auth := setupChannels() -// auth.Test(t) -// defer ts.Close() - -// conf := sdk.Config{ -// ThingsURL: ts.URL, -// } -// mgsdk := sdk.NewSDK(conf) - -// nChannels := uint64(10) -// aChannels := []sdk.Channel{} - -// for i := uint64(1); i < nChannels; i++ { -// channel := sdk.Channel{ -// ID: generateUUID(t), -// Name: fmt.Sprintf("membership_%d@example.com", i), -// Metadata: sdk.Metadata{"role": "channel"}, -// Status: mgclients.EnabledStatus.String(), -// } -// aChannels = append(aChannels, channel) -// } - -// cases := []struct { -// desc string -// token string -// clientID string -// page sdk.PageMetadata -// response []sdk.Channel -// err errors.SDKError -// }{ -// { -// desc: "list channel with authorized token", -// token: validToken, -// clientID: testsutil.GenerateUUID(t), -// page: sdk.PageMetadata{}, -// response: aChannels, -// err: nil, -// }, -// { -// desc: "list channel with offset and limit", -// token: validToken, -// clientID: testsutil.GenerateUUID(t), -// page: sdk.PageMetadata{ -// Offset: 6, -// Total: nChannels, -// Limit: nChannels, -// Status: mgclients.AllStatus.String(), -// }, -// response: aChannels[6 : nChannels-1], -// err: nil, -// }, -// { -// desc: "list channel with given name", -// token: validToken, -// clientID: testsutil.GenerateUUID(t), -// page: sdk.PageMetadata{ -// Name: gName, -// Offset: 6, -// Total: nChannels, -// Limit: nChannels, -// Status: mgclients.AllStatus.String(), -// }, -// response: aChannels[6 : nChannels-1], -// err: nil, -// }, -// { -// desc: "list channel with given level", -// token: validToken, -// clientID: testsutil.GenerateUUID(t), -// page: sdk.PageMetadata{ -// Level: 1, -// Offset: 6, -// Total: nChannels, -// Limit: nChannels, -// Status: mgclients.AllStatus.String(), -// }, -// response: aChannels[6 : nChannels-1], -// err: nil, -// }, -// { -// desc: "list channel with metadata", -// token: validToken, -// clientID: testsutil.GenerateUUID(t), -// page: sdk.PageMetadata{ -// Metadata: validMetadata, -// Offset: 6, -// Total: nChannels, -// Limit: nChannels, -// Status: mgclients.AllStatus.String(), -// }, -// response: aChannels[6 : nChannels-1], -// err: nil, -// }, -// { -// desc: "list channel with an invalid token", -// token: invalidToken, -// clientID: testsutil.GenerateUUID(t), -// page: sdk.PageMetadata{}, -// response: []sdk.Channel(nil), -// err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), -// }, -// } - -// for _, tc := range cases { -// repoCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: tc.token}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil) -// repoCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) -// repoCall2 := auth.On("ListAllSubjects", mock.Anything, mock.Anything).Return(&magistrala.ListSubjectsRes{Policies: toIDs(tc.response)}, nil) -// repoCall3 := auth.On("ListAllObjects", mock.Anything, mock.Anything).Return(&magistrala.ListObjectsRes{Policies: toIDs(tc.response)}, nil) -// repoCall4 := grepo.On("RetrieveByIDs", mock.Anything, mock.Anything, mock.Anything).Return(mggroups.Page{Groups: convertChannels(tc.response)}, tc.err) -// page, err := mgsdk.ChannelsByThing(tc.clientID, tc.page, tc.token) -// assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected error %s, got %s", tc.desc, tc.err, err)) -// assert.Equal(t, tc.response, page.Channels, fmt.Sprintf("%s: expected %v got %v\n", tc.desc, tc.response, page.Channels)) -// repoCall.Unset() -// repoCall1.Unset() -// repoCall2.Unset() -// repoCall3.Unset() -// repoCall4.Unset() -// } -// } - -// func TestEnableChannel(t *testing.T) { -// ts, grepo, auth := setupChannels() -// defer ts.Close() - -// conf := sdk.Config{ -// ThingsURL: ts.URL, -// } -// mgsdk := sdk.NewSDK(conf) - -// creationTime := time.Now().UTC() -// channel := sdk.Channel{ -// ID: generateUUID(t), -// Name: gName, -// CreatedAt: creationTime, -// UpdatedAt: creationTime, -// Status: mgclients.Disabled, -// } - -// repoCall := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) -// repoCall1 := grepo.On("RetrieveByID", mock.Anything, mock.Anything).Return(mggroups.Group{}, repoerr.ErrNotFound) -// repoCall2 := grepo.On("ChangeStatus", mock.Anything, mock.Anything).Return(nil) -// _, err := mgsdk.EnableChannel("wrongID", validToken) -// assert.Equal(t, errors.NewSDKErrorWithStatus(svcerr.ErrViewEntity, http.StatusBadRequest), err, fmt.Sprintf("Enable channel with wrong id: expected %v got %v", svcerr.ErrViewEntity, err)) -// ok := repoCall1.Parent.AssertCalled(t, "RetrieveByID", mock.Anything, "wrongID") -// assert.True(t, ok, "RetrieveByID was not called on enabling channel") -// repoCall.Unset() -// repoCall1.Unset() -// repoCall2.Unset() - -// ch := mggroups.Group{ -// ID: channel.ID, -// Name: channel.Name, -// CreatedAt: creationTime, -// UpdatedAt: creationTime, -// Status: mgclients.DisabledStatus, -// } -// repoCall = auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) -// repoCall1 = grepo.On("RetrieveByID", mock.Anything, mock.Anything).Return(ch, nil) -// repoCall2 = grepo.On("ChangeStatus", mock.Anything, mock.Anything).Return(ch, nil) -// res, err := mgsdk.EnableChannel(channel.ID, validToken) -// assert.Nil(t, err, fmt.Sprintf("Enable channel with correct id: expected %v got %v", nil, err)) -// assert.Equal(t, channel, res, fmt.Sprintf("Enable channel with correct id: expected %v got %v", channel, res)) -// ok = repoCall1.Parent.AssertCalled(t, "RetrieveByID", mock.Anything, channel.ID) -// assert.True(t, ok, "RetrieveByID was not called on enabling channel") -// ok = repoCall2.Parent.AssertCalled(t, "ChangeStatus", mock.Anything, mock.Anything) -// assert.True(t, ok, "ChangeStatus was not called on enabling channel") -// repoCall.Unset() -// repoCall1.Unset() -// repoCall2.Unset() -// } - -// func TestDisableChannel(t *testing.T) { -// ts, grepo, auth := setupChannels() -// defer ts.Close() - -// conf := sdk.Config{ -// ThingsURL: ts.URL, -// } -// mgsdk := sdk.NewSDK(conf) - -// creationTime := time.Now().UTC() -// channel := sdk.Channel{ -// ID: generateUUID(t), -// Name: gName, -// DomainID: generateUUID(t), -// CreatedAt: creationTime, -// UpdatedAt: creationTime, -// Status: mgclients.Enabled, -// } - -// repoCall := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) -// repoCall1 := grepo.On("ChangeStatus", mock.Anything, mock.Anything).Return(nil) -// repoCall2 := grepo.On("RetrieveByID", mock.Anything, mock.Anything).Return(mggroups.Group{}, repoerr.ErrNotFound) -// _, err := mgsdk.DisableChannel("wrongID", validToken) -// assert.Equal(t, err, errors.NewSDKErrorWithStatus(svcerr.ErrViewEntity, http.StatusBadRequest), fmt.Sprintf("Disable channel with wrong id: expected %v got %v", svcerr.ErrNotFound, err)) -// ok := repoCall1.Parent.AssertCalled(t, "RetrieveByID", mock.Anything, "wrongID") -// assert.True(t, ok, "Memberships was not called on disabling channel with wrong id") -// repoCall.Unset() -// repoCall1.Unset() -// repoCall2.Unset() - -// ch := mggroups.Group{ -// ID: channel.ID, -// Name: channel.Name, -// Domain: channel.DomainID, -// CreatedAt: creationTime, -// UpdatedAt: creationTime, -// Status: mgclients.EnabledStatus, -// } - -// repoCall = auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) -// repoCall1 = grepo.On("ChangeStatus", mock.Anything, mock.Anything).Return(ch, nil) -// repoCall2 = grepo.On("RetrieveByID", mock.Anything, mock.Anything).Return(ch, nil) -// res, err := mgsdk.DisableChannel(channel.ID, validToken) -// assert.Nil(t, err, fmt.Sprintf("Disable channel with correct id: expected %v got %v", nil, err)) -// assert.Equal(t, channel, res, fmt.Sprintf("Disable channel with correct id: expected %v got %v", channel, res)) -// ok = repoCall1.Parent.AssertCalled(t, "RetrieveByID", mock.Anything, channel.ID) -// assert.True(t, ok, "RetrieveByID was not called on disabling channel with correct id") -// ok = repoCall2.Parent.AssertCalled(t, "ChangeStatus", mock.Anything, mock.Anything) -// assert.True(t, ok, "ChangeStatus was not called on disabling channel with correct id") -// repoCall.Unset() -// repoCall1.Unset() -// repoCall2.Unset() -// } - -// func TestDeleteChannel(t *testing.T) { -// ts, grepo, auth := setupChannels() -// defer ts.Close() - -// conf := sdk.Config{ -// ThingsURL: ts.URL, -// } -// mgsdk := sdk.NewSDK(conf) - -// creationTime := time.Now().UTC() -// channel := sdk.Channel{ -// ID: generateUUID(t), -// Name: gName, -// CreatedAt: creationTime, -// UpdatedAt: creationTime, -// Status: mgclients.Enabled, -// } - -// repoCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil) -// repoCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: false}, nil) -// repoCall2 := grepo.On("Delete", mock.Anything, mock.Anything).Return(nil) -// err := mgsdk.DeleteChannel("wrongID", validToken) -// assert.Equal(t, err, errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), fmt.Sprintf("Delete channel with wrong id: expected %v got %v", svcerr.ErrNotFound, err)) -// repoCall.Unset() -// repoCall1.Unset() -// repoCall2.Unset() - -// repoCall = auth.On("DeletePolicy", mock.Anything, mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: true}, nil) -// repoCall1 = auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil) -// repoCall2 = auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil) -// repoCall3 := grepo.On("Delete", mock.Anything, mock.Anything).Return(nil) -// err = mgsdk.DeleteChannel(channel.ID, validToken) -// assert.Nil(t, err, fmt.Sprintf("Delete channel with correct id: expected %v got %v", nil, err)) -// ok := repoCall3.Parent.AssertCalled(t, "Delete", mock.Anything, channel.ID) -// assert.True(t, ok, "Delete was not called on deleting channel with correct id") -// repoCall.Unset() -// repoCall1.Unset() -// repoCall2.Unset() -// repoCall3.Unset() -// } +func TestViewChannel(t *testing.T) { + ts, gsvc := setupChannels() + defer ts.Close() -func toIDs(objects interface{}) []string { - v := reflect.ValueOf(objects) - if v.Kind() != reflect.Slice { - panic("objects argument must be a slice") + conf := sdk.Config{ + ThingsURL: ts.URL, } - ids := make([]string, v.Len()) - for i := 0; i < v.Len(); i++ { - id := v.Index(i).FieldByName("ID").String() - ids[i] = id + mgsdk := sdk.NewSDK(conf) + + cases := []struct { + desc string + token string + channelID string + svcRes groups.Group + svcErr error + response sdk.Channel + err errors.SDKError + }{ + { + desc: "view channel successfully", + token: validToken, + channelID: group.ID, + svcRes: group, + svcErr: nil, + response: channel, + err: nil, + }, + { + desc: "view channel with invalid token", + token: invalidToken, + channelID: group.ID, + svcRes: groups.Group{}, + svcErr: svcerr.ErrAuthentication, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "view channel with empty token", + token: "", + channelID: group.ID, + svcRes: groups.Group{}, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized), + }, + { + desc: "view channel for wrong id", + token: validToken, + channelID: wrongID, + svcRes: groups.Group{}, + svcErr: svcerr.ErrViewEntity, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrViewEntity, http.StatusBadRequest), + }, + { + desc: "view channel with empty channel id", + token: validToken, + channelID: "", + svcRes: groups.Group{}, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKError(apiutil.ErrMissingID), + }, + { + desc: "view channel with service response that can't be unmarshalled", + token: validToken, + channelID: group.ID, + svcRes: groups.Group{ + ID: generateUUID(t), + Metadata: mgclients.Metadata{ + "test": make(chan int), + }, + }, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKError(errors.New("unexpected end of JSON input")), + }, } - return ids + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("ViewGroup", mock.Anything, tc.token, tc.channelID).Return(tc.svcRes, tc.svcErr) + resp, err := mgsdk.Channel(tc.channelID, tc.token) + assert.Equal(t, tc.err, err) + assert.Equal(t, tc.response, resp) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "ViewGroup", mock.Anything, tc.token, tc.channelID) + assert.True(t, ok) + } + svcCall.Unset() + }) + } +} + +func TestUpdateChannel(t *testing.T) { + ts, gsvc := setupChannels() + defer ts.Close() + + conf := sdk.Config{ + ThingsURL: ts.URL, + } + mgsdk := sdk.NewSDK(conf) + + nGroup := group + nGroup.Name = newName + nChannel := channel + nChannel.Name = newName + + dGroup := group + dGroup.Description = newDescription + dChannel := channel + dChannel.Description = newDescription + + mGroup := group + mGroup.Metadata = mgclients.Metadata{ + "field": "value2", + } + mChannel := channel + mChannel.Metadata = sdk.Metadata{ + "field": "value2", + } + + aGroup := group + aGroup.Name = newName + aGroup.Description = newDescription + aGroup.Metadata = mgclients.Metadata{"field": "value2"} + aChannel := channel + aChannel.Name = newName + aChannel.Description = newDescription + aChannel.Metadata = sdk.Metadata{"field": "value2"} + + cases := []struct { + desc string + token string + channelReq sdk.Channel + updateGroupReq groups.Group + svcRes groups.Group + svcErr error + response sdk.Channel + err errors.SDKError + }{ + { + desc: "update channel name", + token: validToken, + channelReq: sdk.Channel{ + ID: channel.ID, + Name: newName, + }, + updateGroupReq: groups.Group{ + ID: group.ID, + Name: newName, + }, + svcRes: nGroup, + svcErr: nil, + response: nChannel, + err: nil, + }, + { + desc: "update channel description", + token: validToken, + channelReq: sdk.Channel{ + ID: channel.ID, + Description: newDescription, + }, + updateGroupReq: groups.Group{ + ID: group.ID, + Description: newDescription, + }, + svcRes: dGroup, + svcErr: nil, + response: dChannel, + err: nil, + }, + { + desc: "update channel metadata", + token: validToken, + channelReq: sdk.Channel{ + ID: channel.ID, + Metadata: sdk.Metadata{ + "field": "value2", + }, + }, + updateGroupReq: groups.Group{ + ID: group.ID, + Metadata: mgclients.Metadata{"field": "value2"}, + }, + svcRes: mGroup, + svcErr: nil, + response: mChannel, + err: nil, + }, + { + desc: "update channel with every field defined", + token: validToken, + channelReq: sdk.Channel{ + ID: channel.ID, + Name: newName, + Description: newDescription, + Metadata: sdk.Metadata{"field": "value2"}, + }, + updateGroupReq: groups.Group{ + ID: group.ID, + Name: newName, + Description: newDescription, + Metadata: mgclients.Metadata{"field": "value2"}, + }, + svcRes: aGroup, + svcErr: nil, + response: aChannel, + err: nil, + }, + { + desc: "update channel name with invalid channel id", + token: validToken, + channelReq: sdk.Channel{ + ID: wrongID, + Name: newName, + }, + updateGroupReq: groups.Group{ + ID: wrongID, + Name: newName, + }, + svcRes: groups.Group{}, + svcErr: svcerr.ErrNotFound, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound), + }, + { + desc: "update channel description with invalid channel id", + token: validToken, + channelReq: sdk.Channel{ + ID: wrongID, + Description: newDescription, + }, + updateGroupReq: groups.Group{ + ID: wrongID, + Description: newDescription, + }, + svcRes: groups.Group{}, + svcErr: svcerr.ErrNotFound, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound), + }, + { + desc: "update channel metadata with invalid channel id", + token: validToken, + channelReq: sdk.Channel{ + ID: wrongID, + Metadata: sdk.Metadata{ + "field": "value2", + }, + }, + updateGroupReq: groups.Group{ + ID: wrongID, + Metadata: mgclients.Metadata{"field": "value2"}, + }, + svcRes: groups.Group{}, + svcErr: svcerr.ErrNotFound, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound), + }, + { + desc: "update channel with invalid token", + token: invalidToken, + channelReq: sdk.Channel{ + ID: channel.ID, + Name: newName, + }, + updateGroupReq: groups.Group{ + ID: group.ID, + Name: newName, + }, + svcRes: groups.Group{}, + svcErr: svcerr.ErrAuthentication, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "update channel with empty token", + token: "", + channelReq: sdk.Channel{ + ID: channel.ID, + Name: newName, + }, + updateGroupReq: groups.Group{ + ID: group.ID, + Name: newName, + }, + svcRes: groups.Group{}, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized), + }, + { + desc: "update channel with name that is too long", + token: validToken, + channelReq: sdk.Channel{ + ID: channel.ID, + Name: strings.Repeat("a", 1025), + }, + updateGroupReq: groups.Group{}, + svcRes: groups.Group{}, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrNameSize), http.StatusBadRequest), + }, + { + desc: "update channel that can't be marshalled", + token: validToken, + channelReq: sdk.Channel{ + Name: "test", + Metadata: map[string]interface{}{ + "test": make(chan int), + }, + }, + updateGroupReq: groups.Group{}, + svcRes: groups.Group{}, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKError(errors.New("json: unsupported type: chan int")), + }, + { + desc: "update channel with service response that can't be unmarshalled", + token: validToken, + channelReq: sdk.Channel{ + ID: channel.ID, + Name: newName, + }, + updateGroupReq: groups.Group{ + ID: group.ID, + Name: newName, + }, + svcRes: groups.Group{ + ID: generateUUID(t), + Metadata: mgclients.Metadata{ + "test": make(chan int), + }, + }, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKError(errors.New("unexpected end of JSON input")), + }, + { + desc: "update channel with empty channel id", + token: validToken, + channelReq: sdk.Channel{ + Name: newName, + }, + updateGroupReq: groups.Group{}, + svcRes: groups.Group{}, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKError(apiutil.ErrMissingID), + }, + } + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("UpdateGroup", mock.Anything, tc.token, tc.updateGroupReq).Return(tc.svcRes, tc.svcErr) + resp, err := mgsdk.UpdateChannel(tc.channelReq, tc.token) + assert.Equal(t, tc.err, err) + assert.Equal(t, tc.response, resp) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "UpdateGroup", mock.Anything, tc.token, tc.updateGroupReq) + assert.True(t, ok) + } + svcCall.Unset() + }) + } +} + +func TestListChannelsByThing(t *testing.T) { + ts, gsvc := setupChannels() + defer ts.Close() + + conf := sdk.Config{ + ThingsURL: ts.URL, + } + mgsdk := sdk.NewSDK(conf) + + nChannels := uint64(10) + aChannels := []sdk.Channel{} + + for i := uint64(1); i < nChannels; i++ { + channel := sdk.Channel{ + ID: generateUUID(t), + Name: fmt.Sprintf("membership_%d@example.com", i), + Metadata: sdk.Metadata{"role": "channel"}, + Status: mgclients.EnabledStatus.String(), + } + aChannels = append(aChannels, channel) + } + + cases := []struct { + desc string + token string + thingID string + pageMeta sdk.PageMetadata + listGroupsReq groups.Page + svcRes groups.Page + svcErr error + response sdk.ChannelsPage + err errors.SDKError + }{ + { + desc: "list channels successfully", + token: validToken, + thingID: testsutil.GenerateUUID(t), + pageMeta: sdk.PageMetadata{}, + listGroupsReq: groups.Page{ + PageMeta: groups.PageMeta{ + Offset: 0, + Limit: 10, + }, + Permission: "view", + Direction: -1, + }, + svcRes: groups.Page{ + PageMeta: groups.PageMeta{ + Total: nChannels, + }, + Groups: convertChannels(aChannels), + }, + svcErr: nil, + response: sdk.ChannelsPage{ + PageRes: sdk.PageRes{ + Total: nChannels, + }, + Channels: aChannels, + }, + err: nil, + }, + { + desc: "list channel with offset and limit", + token: validToken, + thingID: testsutil.GenerateUUID(t), + pageMeta: sdk.PageMetadata{ + Offset: 6, + Limit: nChannels, + }, + listGroupsReq: groups.Page{ + PageMeta: groups.PageMeta{ + Offset: 6, + Limit: 10, + }, + Permission: "view", + Direction: -1, + }, + svcRes: groups.Page{ + PageMeta: groups.PageMeta{ + Total: uint64(len(aChannels[6 : nChannels-1])), + }, + Groups: convertChannels(aChannels[6 : nChannels-1]), + }, + svcErr: nil, + response: sdk.ChannelsPage{ + PageRes: sdk.PageRes{ + Total: uint64(len(aChannels[6 : nChannels-1])), + }, + Channels: aChannels[6 : nChannels-1], + }, + err: nil, + }, + { + desc: "list channel with given name", + token: validToken, + thingID: testsutil.GenerateUUID(t), + pageMeta: sdk.PageMetadata{ + Name: "membership_8@example.com", + Offset: 0, + Limit: nChannels, + }, + listGroupsReq: groups.Page{ + PageMeta: groups.PageMeta{ + Name: "membership_8@example.com", + Offset: 0, + Limit: nChannels, + }, + Permission: "view", + Direction: -1, + }, + svcRes: groups.Page{ + PageMeta: groups.PageMeta{ + Total: 1, + }, + Groups: convertChannels([]sdk.Channel{aChannels[8]}), + }, + svcErr: nil, + response: sdk.ChannelsPage{ + PageRes: sdk.PageRes{ + Total: 1, + }, + Channels: aChannels[8:9], + }, + err: nil, + }, + { + desc: "list channels with invalid token", + token: invalidToken, + thingID: testsutil.GenerateUUID(t), + pageMeta: sdk.PageMetadata{}, + listGroupsReq: groups.Page{ + PageMeta: groups.PageMeta{ + Offset: 0, + Limit: 10, + }, + Permission: "view", + Direction: -1, + }, + svcRes: groups.Page{}, + svcErr: svcerr.ErrAuthentication, + response: sdk.ChannelsPage{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "list channels with empty token", + token: "", + thingID: testsutil.GenerateUUID(t), + pageMeta: sdk.PageMetadata{}, + listGroupsReq: groups.Page{}, + svcRes: groups.Page{}, + svcErr: nil, + response: sdk.ChannelsPage{}, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized), + }, + { + desc: "list channels with limit greater than max", + token: validToken, + thingID: testsutil.GenerateUUID(t), + pageMeta: sdk.PageMetadata{ + Limit: 110, + }, + listGroupsReq: groups.Page{}, + svcRes: groups.Page{}, + svcErr: nil, + response: sdk.ChannelsPage{}, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrLimitSize), http.StatusBadRequest), + }, + { + desc: "list channels with invalid metadata", + token: validToken, + thingID: testsutil.GenerateUUID(t), + pageMeta: sdk.PageMetadata{ + Metadata: sdk.Metadata{ + "test": make(chan int), + }, + }, + listGroupsReq: groups.Page{}, + svcRes: groups.Page{}, + svcErr: nil, + response: sdk.ChannelsPage{}, + err: errors.NewSDKError(errors.New("json: unsupported type: chan int")), + }, + { + desc: "list channels with service response that can't be unmarshalled", + token: validToken, + thingID: testsutil.GenerateUUID(t), + pageMeta: sdk.PageMetadata{ + Offset: 0, + Limit: 10, + }, + listGroupsReq: groups.Page{ + PageMeta: groups.PageMeta{ + Offset: 0, + Limit: 10, + }, + Permission: "view", + Direction: -1, + }, + svcRes: groups.Page{ + PageMeta: groups.PageMeta{ + Total: 1, + }, + Groups: []groups.Group{{ + ID: generateUUID(t), + Metadata: mgclients.Metadata{ + "test": make(chan int), + }, + }}, + }, + svcErr: nil, + response: sdk.ChannelsPage{}, + err: errors.NewSDKError(errors.New("unexpected end of JSON input")), + }, + } + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("ListGroups", mock.Anything, tc.token, auth.ThingsKind, tc.thingID, tc.listGroupsReq).Return(tc.svcRes, tc.svcErr) + resp, err := mgsdk.ChannelsByThing(tc.thingID, tc.pageMeta, tc.token) + assert.Equal(t, tc.err, err) + assert.Equal(t, tc.response, resp) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "ListGroups", mock.Anything, tc.token, auth.ThingsKind, tc.thingID, tc.listGroupsReq) + assert.True(t, ok) + } + svcCall.Unset() + }) + } +} + +func TestEnableChannel(t *testing.T) { + ts, gsvc := setupChannels() + defer ts.Close() + + conf := sdk.Config{ + ThingsURL: ts.URL, + } + mgsdk := sdk.NewSDK(conf) + + cases := []struct { + desc string + token string + channelID string + svcRes groups.Group + svcErr error + response sdk.Channel + err errors.SDKError + }{ + { + desc: "enable channel successfully", + token: validToken, + channelID: channel.ID, + svcRes: group, + svcErr: nil, + response: channel, + err: nil, + }, + { + desc: "enable channel with invalid token", + token: invalidToken, + channelID: channel.ID, + svcRes: groups.Group{}, + svcErr: svcerr.ErrAuthentication, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "enable channel with empty token", + token: "", + channelID: channel.ID, + svcRes: groups.Group{}, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized), + }, + { + desc: "enable channel with invalid channel id", + token: validToken, + channelID: wrongID, + svcRes: groups.Group{}, + svcErr: svcerr.ErrNotFound, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound), + }, + { + desc: "enable channel with empty channel id", + token: validToken, + channelID: "", + svcRes: groups.Group{}, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest), + }, + { + desc: "enable channel with service response that can't be unmarshalled", + token: validToken, + channelID: channel.ID, + svcRes: groups.Group{ + ID: generateUUID(t), + Metadata: mgclients.Metadata{ + "test": make(chan int), + }, + }, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKError(errors.New("unexpected end of JSON input")), + }, + } + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("EnableGroup", mock.Anything, tc.token, tc.channelID).Return(tc.svcRes, tc.svcErr) + resp, err := mgsdk.EnableChannel(tc.channelID, tc.token) + assert.Equal(t, tc.err, err) + assert.Equal(t, tc.response, resp) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "EnableGroup", mock.Anything, tc.token, tc.channelID) + assert.True(t, ok) + } + svcCall.Unset() + }) + } +} + +func TestDisableChannel(t *testing.T) { + ts, gsvc := setupChannels() + defer ts.Close() + + conf := sdk.Config{ + ThingsURL: ts.URL, + } + mgsdk := sdk.NewSDK(conf) + + dGroup := group + dGroup.Status = mgclients.DisabledStatus + dChannel := channel + dChannel.Status = mgclients.DisabledStatus.String() + + cases := []struct { + desc string + token string + channelID string + svcRes groups.Group + svcErr error + response sdk.Channel + err errors.SDKError + }{ + { + desc: "disable channel successfully", + token: validToken, + channelID: channel.ID, + svcRes: dGroup, + svcErr: nil, + response: dChannel, + err: nil, + }, + { + desc: "disable channel with invalid token", + token: invalidToken, + channelID: channel.ID, + svcRes: groups.Group{}, + svcErr: svcerr.ErrAuthentication, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "disable channel with empty token", + token: "", + channelID: channel.ID, + svcRes: groups.Group{}, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized), + }, + { + desc: "disable channel with invalid channel id", + token: validToken, + channelID: wrongID, + svcRes: groups.Group{}, + svcErr: svcerr.ErrNotFound, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound), + }, + { + desc: "disable channel with empty channel id", + token: validToken, + channelID: "", + svcRes: groups.Group{}, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest), + }, + { + desc: "disable channel with service response that can't be unmarshalled", + token: validToken, + channelID: channel.ID, + svcRes: groups.Group{ + ID: generateUUID(t), + Metadata: mgclients.Metadata{ + "test": make(chan int), + }, + }, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKError(errors.New("unexpected end of JSON input")), + }, + } + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("DisableGroup", mock.Anything, tc.token, tc.channelID).Return(tc.svcRes, tc.svcErr) + resp, err := mgsdk.DisableChannel(tc.channelID, tc.token) + assert.Equal(t, tc.err, err) + assert.Equal(t, tc.response, resp) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "DisableGroup", mock.Anything, tc.token, tc.channelID) + assert.True(t, ok) + } + svcCall.Unset() + }) + } +} + +func TestDeleteChannel(t *testing.T) { + ts, gsvc := setupChannels() + defer ts.Close() + + conf := sdk.Config{ + ThingsURL: ts.URL, + } + mgsdk := sdk.NewSDK(conf) + + cases := []struct { + desc string + token string + channelID string + svcErr error + err errors.SDKError + }{ + { + desc: "delete channel successfully", + token: validToken, + channelID: channel.ID, + svcErr: nil, + err: nil, + }, + { + desc: "delete channel with invalid token", + token: invalidToken, + channelID: channel.ID, + svcErr: svcerr.ErrAuthentication, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "delete channel with empty token", + token: "", + channelID: channel.ID, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized), + }, + { + desc: "delete channel with invalid channel id", + token: validToken, + channelID: wrongID, + svcErr: svcerr.ErrRemoveEntity, + err: errors.NewSDKErrorWithStatus(svcerr.ErrRemoveEntity, http.StatusUnprocessableEntity), + }, + { + desc: "delete channel with empty channel id", + token: validToken, + channelID: "", + svcErr: svcerr.ErrRemoveEntity, + err: errors.NewSDKError(apiutil.ErrMissingID), + }, + } + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("DeleteGroup", mock.Anything, tc.token, tc.channelID).Return(tc.svcErr) + err := mgsdk.DeleteChannel(tc.channelID, tc.token) + assert.Equal(t, tc.err, err) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "DeleteGroup", mock.Anything, tc.token, tc.channelID) + assert.True(t, ok) + } + svcCall.Unset() + }) + } +} + +func TestChannelPermissions(t *testing.T) { + ts, gsvc := setupChannels() + defer ts.Close() + + conf := sdk.Config{ + ThingsURL: ts.URL, + } + mgsdk := sdk.NewSDK(conf) + + cases := []struct { + desc string + token string + channelID string + svcRes []string + svcErr error + response sdk.Channel + err errors.SDKError + }{ + { + desc: "view channel permissions successfully", + token: validToken, + channelID: channel.ID, + svcRes: []string{"view"}, + svcErr: nil, + response: sdk.Channel{ + Permissions: []string{"view"}, + }, + err: nil, + }, + { + desc: "view channel permissions with invalid token", + token: invalidToken, + channelID: channel.ID, + svcRes: []string{}, + svcErr: svcerr.ErrAuthentication, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "view channel permissions with empty token", + token: "", + channelID: channel.ID, + svcRes: []string{}, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized), + }, + { + desc: "view channel permissions with invalid channel id", + token: validToken, + channelID: wrongID, + svcRes: []string{}, + svcErr: svcerr.ErrAuthorization, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + }, + { + desc: "view channel permissions with empty channel id", + token: validToken, + channelID: "", + svcRes: []string{}, + svcErr: nil, + response: sdk.Channel{}, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest), + }, + } + + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("ViewGroupPerms", mock.Anything, tc.token, tc.channelID).Return(tc.svcRes, tc.svcErr) + resp, err := mgsdk.ChannelPermissions(tc.channelID, tc.token) + assert.Equal(t, tc.err, err) + assert.Equal(t, tc.response, resp) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "ViewGroupPerms", mock.Anything, tc.token, tc.channelID) + assert.True(t, ok) + } + svcCall.Unset() + }) + } +} + +func TestAddUserToChannel(t *testing.T) { + ts, gsvc := setupChannels() + defer ts.Close() + + conf := sdk.Config{ + ThingsURL: ts.URL, + } + mgsdk := sdk.NewSDK(conf) + + cases := []struct { + desc string + token string + channelID string + addUserReq sdk.UsersRelationRequest + svcErr error + err errors.SDKError + }{ + { + desc: "add user to channel successfully", + token: validToken, + channelID: channel.ID, + addUserReq: sdk.UsersRelationRequest{ + Relation: "member", + UserIDs: []string{user.ID}, + }, + svcErr: nil, + err: nil, + }, + { + desc: "add user to channel with invalid token", + token: invalidToken, + channelID: channel.ID, + addUserReq: sdk.UsersRelationRequest{ + Relation: "member", + UserIDs: []string{user.ID}, + }, + svcErr: svcerr.ErrAuthentication, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "add user to channel with empty token", + token: "", + channelID: channel.ID, + addUserReq: sdk.UsersRelationRequest{ + Relation: "member", + UserIDs: []string{user.ID}, + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized), + }, + { + desc: "add user to channel with invalid channel id", + token: validToken, + channelID: wrongID, + addUserReq: sdk.UsersRelationRequest{ + Relation: "member", + UserIDs: []string{user.ID}, + }, + svcErr: svcerr.ErrAuthorization, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + }, + { + desc: "add user to channel with empty channel id", + token: validToken, + channelID: "", + addUserReq: sdk.UsersRelationRequest{ + Relation: "member", + UserIDs: []string{user.ID}, + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest), + }, + { + desc: "add users to channel with empty relation", + token: validToken, + channelID: channel.ID, + addUserReq: sdk.UsersRelationRequest{ + Relation: "", + UserIDs: []string{user.ID}, + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingRelation), http.StatusBadRequest), + }, + { + desc: "add users to channel with empty user ids", + token: validToken, + channelID: channel.ID, + addUserReq: sdk.UsersRelationRequest{ + Relation: "member", + UserIDs: []string{}, + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrEmptyList), http.StatusBadRequest), + }, + } + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("Assign", mock.Anything, tc.token, tc.channelID, tc.addUserReq.Relation, auth.UsersKind, tc.addUserReq.UserIDs).Return(tc.svcErr) + err := mgsdk.AddUserToChannel(tc.channelID, tc.addUserReq, tc.token) + assert.Equal(t, tc.err, err) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "Assign", mock.Anything, tc.token, tc.channelID, tc.addUserReq.Relation, auth.UsersKind, tc.addUserReq.UserIDs) + assert.True(t, ok) + } + svcCall.Unset() + }) + } +} + +func TestRemoveUserFromChannel(t *testing.T) { + ts, gsvc := setupChannels() + defer ts.Close() + + conf := sdk.Config{ + ThingsURL: ts.URL, + } + mgsdk := sdk.NewSDK(conf) + + cases := []struct { + desc string + token string + channelID string + removeUserReq sdk.UsersRelationRequest + svcErr error + err errors.SDKError + }{ + { + desc: "remove user from channel successfully", + token: validToken, + channelID: channel.ID, + removeUserReq: sdk.UsersRelationRequest{ + Relation: "member", + UserIDs: []string{user.ID}, + }, + svcErr: nil, + err: nil, + }, + { + desc: "remove user from channel with invalid token", + token: invalidToken, + channelID: channel.ID, + removeUserReq: sdk.UsersRelationRequest{ + Relation: "member", + UserIDs: []string{user.ID}, + }, + svcErr: svcerr.ErrAuthentication, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "remove user from channel with empty token", + token: "", + channelID: channel.ID, + removeUserReq: sdk.UsersRelationRequest{ + Relation: "member", + UserIDs: []string{user.ID}, + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized), + }, + { + desc: "remove user from channel with invalid channel id", + token: validToken, + channelID: wrongID, + removeUserReq: sdk.UsersRelationRequest{ + Relation: "member", + UserIDs: []string{user.ID}, + }, + svcErr: svcerr.ErrAuthorization, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + }, + { + desc: "remove user from channel with empty channel id", + token: validToken, + channelID: "", + removeUserReq: sdk.UsersRelationRequest{ + Relation: "member", + UserIDs: []string{user.ID}, + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest), + }, + { + desc: "remove users from channel with empty user ids", + token: validToken, + channelID: channel.ID, + removeUserReq: sdk.UsersRelationRequest{ + Relation: "member", + UserIDs: []string{}, + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrEmptyList), http.StatusBadRequest), + }, + } + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("Unassign", mock.Anything, tc.token, tc.channelID, tc.removeUserReq.Relation, auth.UsersKind, tc.removeUserReq.UserIDs).Return(tc.svcErr) + err := mgsdk.RemoveUserFromChannel(tc.channelID, tc.removeUserReq, tc.token) + assert.Equal(t, tc.err, err) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "Unassign", mock.Anything, tc.token, tc.channelID, tc.removeUserReq.Relation, auth.UsersKind, tc.removeUserReq.UserIDs) + assert.True(t, ok) + } + svcCall.Unset() + }) + } +} + +func TestAddUserGroupToChannel(t *testing.T) { + ts, gsvc := setupChannels() + defer ts.Close() + + conf := sdk.Config{ + ThingsURL: ts.URL, + } + mgsdk := sdk.NewSDK(conf) + + relation := "parent_group" + + groupID := generateUUID(t) + + cases := []struct { + desc string + token string + channelID string + addUserGroupReq sdk.UserGroupsRequest + svcErr error + err errors.SDKError + }{ + { + desc: "add user group to channel successfully", + token: validToken, + channelID: channel.ID, + addUserGroupReq: sdk.UserGroupsRequest{ + UserGroupIDs: []string{groupID}, + }, + svcErr: nil, + err: nil, + }, + { + desc: "add user group to channel with invalid token", + token: invalidToken, + channelID: channel.ID, + addUserGroupReq: sdk.UserGroupsRequest{ + UserGroupIDs: []string{groupID}, + }, + svcErr: svcerr.ErrAuthentication, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "add user group to channel with empty token", + token: "", + channelID: channel.ID, + addUserGroupReq: sdk.UserGroupsRequest{ + UserGroupIDs: []string{groupID}, + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized), + }, + { + desc: "add user group to channel with invalid channel id", + token: validToken, + channelID: wrongID, + addUserGroupReq: sdk.UserGroupsRequest{ + UserGroupIDs: []string{groupID}, + }, + svcErr: svcerr.ErrAuthorization, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + }, + { + desc: "add user group to channel with empty channel id", + token: validToken, + channelID: "", + addUserGroupReq: sdk.UserGroupsRequest{ + UserGroupIDs: []string{groupID}, + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest), + }, + { + desc: "add user group to channel with empty group ids", + token: validToken, + channelID: channel.ID, + addUserGroupReq: sdk.UserGroupsRequest{ + UserGroupIDs: []string{}, + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrEmptyList), http.StatusBadRequest), + }, + } + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("Assign", mock.Anything, tc.token, tc.channelID, relation, auth.ChannelsKind, tc.addUserGroupReq.UserGroupIDs).Return(tc.svcErr) + err := mgsdk.AddUserGroupToChannel(tc.channelID, tc.addUserGroupReq, tc.token) + assert.Equal(t, tc.err, err) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "Assign", mock.Anything, tc.token, tc.channelID, relation, auth.ChannelsKind, tc.addUserGroupReq.UserGroupIDs) + assert.True(t, ok) + } + svcCall.Unset() + }) + } +} + +func TestRemoveUserGroupFromChannel(t *testing.T) { + ts, gsvc := setupChannels() + defer ts.Close() + + conf := sdk.Config{ + ThingsURL: ts.URL, + } + mgsdk := sdk.NewSDK(conf) + + relation := "parent_group" + + groupID := generateUUID(t) + + cases := []struct { + desc string + token string + channelID string + removeUserGroupReq sdk.UserGroupsRequest + svcErr error + err errors.SDKError + }{ + { + desc: "remove user group from channel successfully", + token: validToken, + channelID: channel.ID, + removeUserGroupReq: sdk.UserGroupsRequest{ + UserGroupIDs: []string{groupID}, + }, + svcErr: nil, + err: nil, + }, + { + desc: "remove user group from channel with invalid token", + token: invalidToken, + channelID: channel.ID, + removeUserGroupReq: sdk.UserGroupsRequest{ + UserGroupIDs: []string{groupID}, + }, + svcErr: svcerr.ErrAuthentication, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "remove user group from channel with empty token", + token: "", + channelID: channel.ID, + removeUserGroupReq: sdk.UserGroupsRequest{ + UserGroupIDs: []string{groupID}, + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized), + }, + { + desc: "remove user group from channel with invalid channel id", + token: validToken, + channelID: wrongID, + removeUserGroupReq: sdk.UserGroupsRequest{ + UserGroupIDs: []string{groupID}, + }, + svcErr: svcerr.ErrAuthorization, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + }, + { + desc: "remove user group from channel with empty channel id", + token: validToken, + channelID: "", + removeUserGroupReq: sdk.UserGroupsRequest{ + UserGroupIDs: []string{groupID}, + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest), + }, + { + desc: "remove user group from channel with empty group ids", + token: validToken, + channelID: channel.ID, + removeUserGroupReq: sdk.UserGroupsRequest{ + UserGroupIDs: []string{}, + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrEmptyList), http.StatusBadRequest), + }, + } + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("Unassign", mock.Anything, tc.token, tc.channelID, relation, auth.ChannelsKind, tc.removeUserGroupReq.UserGroupIDs).Return(tc.svcErr) + err := mgsdk.RemoveUserGroupFromChannel(tc.channelID, tc.removeUserGroupReq, tc.token) + assert.Equal(t, tc.err, err) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "Unassign", mock.Anything, tc.token, tc.channelID, relation, auth.ChannelsKind, tc.removeUserGroupReq.UserGroupIDs) + assert.True(t, ok) + } + svcCall.Unset() + }) + } +} + +func TestListChannelUserGroups(t *testing.T) { + ts, gsvc := setupChannels() + defer ts.Close() + + conf := sdk.Config{ + UsersURL: ts.URL, + } + mgsdk := sdk.NewSDK(conf) + + nGroups := uint64(10) + aGroups := []sdk.Group{} + + for i := uint64(1); i < nGroups; i++ { + group := sdk.Group{ + ID: generateUUID(t), + Name: fmt.Sprintf("group_%d", i), + Metadata: sdk.Metadata{"role": "group"}, + Status: mgclients.EnabledStatus.String(), + } + aGroups = append(aGroups, group) + } + + cases := []struct { + desc string + token string + channelID string + pageMeta sdk.PageMetadata + listGroupsReq groups.Page + svcRes groups.Page + svcErr error + response sdk.GroupsPage + err errors.SDKError + }{ + { + desc: "list user groups successfully", + token: validToken, + channelID: channel.ID, + pageMeta: sdk.PageMetadata{}, + listGroupsReq: groups.Page{ + PageMeta: groups.PageMeta{ + Offset: 0, + Limit: 10, + }, + Permission: "view", + Direction: -1, + }, + svcRes: groups.Page{ + PageMeta: groups.PageMeta{ + Total: nGroups, + }, + Groups: convertGroups(aGroups), + }, + svcErr: nil, + response: sdk.GroupsPage{ + PageRes: sdk.PageRes{ + Total: nGroups, + }, + Groups: aGroups, + }, + err: nil, + }, + { + desc: "list user groups with offset and limit", + token: validToken, + channelID: channel.ID, + pageMeta: sdk.PageMetadata{ + Offset: 6, + Limit: nGroups, + }, + listGroupsReq: groups.Page{ + PageMeta: groups.PageMeta{ + Offset: 6, + Limit: 10, + }, + Permission: "view", + Direction: -1, + }, + svcRes: groups.Page{ + PageMeta: groups.PageMeta{ + Total: uint64(len(aGroups[6 : nGroups-1])), + }, + Groups: convertGroups(aGroups[6 : nGroups-1]), + }, + svcErr: nil, + response: sdk.GroupsPage{ + PageRes: sdk.PageRes{ + Total: uint64(len(aGroups[6 : nGroups-1])), + }, + Groups: aGroups[6 : nGroups-1], + }, + err: nil, + }, + { + desc: "list user groups with invalid token", + token: invalidToken, + channelID: channel.ID, + pageMeta: sdk.PageMetadata{}, + listGroupsReq: groups.Page{ + PageMeta: groups.PageMeta{ + Offset: 0, + Limit: 10, + }, + Permission: "view", + Direction: -1, + }, + svcRes: groups.Page{}, + svcErr: svcerr.ErrAuthentication, + response: sdk.GroupsPage{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "list user groups with empty token", + token: "", + channelID: channel.ID, + pageMeta: sdk.PageMetadata{}, + listGroupsReq: groups.Page{ + PageMeta: groups.PageMeta{ + Offset: 0, + Limit: 10, + }, + Permission: "view", + Direction: -1, + }, + svcRes: groups.Page{}, + svcErr: nil, + response: sdk.GroupsPage{}, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized), + }, + { + desc: "list user groups with limit greater than max", + token: validToken, + channelID: channel.ID, + pageMeta: sdk.PageMetadata{ + Limit: 110, + }, + listGroupsReq: groups.Page{}, + svcRes: groups.Page{}, + svcErr: nil, + response: sdk.GroupsPage{}, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrLimitSize), http.StatusBadRequest), + }, + { + desc: "list user groups with invalid channel id", + token: validToken, + channelID: wrongID, + pageMeta: sdk.PageMetadata{}, + listGroupsReq: groups.Page{ + PageMeta: groups.PageMeta{ + Offset: 0, + Limit: 10, + }, + Permission: "view", + Direction: -1, + }, + svcRes: groups.Page{}, + svcErr: svcerr.ErrAuthorization, + response: sdk.GroupsPage{}, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + }, + { + desc: "list users groups with level exceeding max", + token: validToken, + channelID: channel.ID, + pageMeta: sdk.PageMetadata{ + Level: 10, + }, + listGroupsReq: groups.Page{}, + svcRes: groups.Page{}, + svcErr: nil, + response: sdk.GroupsPage{}, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrInvalidLevel), http.StatusInternalServerError), + }, + { + desc: "list users with invalid page metadata", + token: validToken, + channelID: channel.ID, + pageMeta: sdk.PageMetadata{ + Offset: 0, + Limit: 10, + Metadata: sdk.Metadata{ + "test": make(chan int), + }, + }, + listGroupsReq: groups.Page{}, + svcRes: groups.Page{}, + svcErr: nil, + response: sdk.GroupsPage{}, + err: errors.NewSDKError(errors.New("json: unsupported type: chan int")), + }, + { + desc: "list user groups with service response that can't be unmarshalled", + token: validToken, + channelID: channel.ID, + pageMeta: sdk.PageMetadata{}, + listGroupsReq: groups.Page{ + PageMeta: groups.PageMeta{ + Offset: 0, + Limit: 10, + }, + Permission: "view", + Direction: -1, + }, + svcRes: groups.Page{ + PageMeta: groups.PageMeta{ + Total: 1, + }, + Groups: []groups.Group{ + { + ID: generateUUID(t), + Metadata: mgclients.Metadata{"test": make(chan int)}, + }, + }, + }, + svcErr: nil, + response: sdk.GroupsPage{}, + err: errors.NewSDKError(errors.New("unexpected end of JSON input")), + }, + } + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("ListGroups", mock.Anything, tc.token, auth.ChannelsKind, tc.channelID, tc.listGroupsReq).Return(tc.svcRes, tc.svcErr) + resp, err := mgsdk.ListChannelUserGroups(tc.channelID, tc.pageMeta, tc.token) + assert.Equal(t, tc.err, err) + assert.Equal(t, tc.response, resp) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "ListGroups", mock.Anything, tc.token, auth.ChannelsKind, tc.channelID, tc.listGroupsReq) + assert.True(t, ok) + } + svcCall.Unset() + }) + } +} + +func TestConnect(t *testing.T) { + ts, gsvc := setupChannels() + defer ts.Close() + + conf := sdk.Config{ + ThingsURL: ts.URL, + } + mgsdk := sdk.NewSDK(conf) + + thingID := generateUUID(t) + + cases := []struct { + desc string + token string + connection sdk.Connection + svcErr error + err errors.SDKError + }{ + { + desc: "connect successfully", + token: validToken, + connection: sdk.Connection{ + ChannelID: channel.ID, + ThingID: thingID, + }, + svcErr: nil, + err: nil, + }, + { + desc: "connect with invalid token", + token: invalidToken, + connection: sdk.Connection{ + ChannelID: channel.ID, + ThingID: thingID, + }, + svcErr: svcerr.ErrAuthentication, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "connect with empty token", + token: "", + connection: sdk.Connection{ + ChannelID: channel.ID, + ThingID: thingID, + }, + svcErr: svcerr.ErrAuthentication, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "connect with invalid channel id", + token: validToken, + connection: sdk.Connection{ + ChannelID: wrongID, + ThingID: thingID, + }, + svcErr: svcerr.ErrAuthorization, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + }, + { + desc: "connect with empty channel id", + token: validToken, + connection: sdk.Connection{ + ChannelID: "", + ThingID: thingID, + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest), + }, + { + desc: "connect with empty thing id", + token: validToken, + connection: sdk.Connection{ + ChannelID: channel.ID, + ThingID: "", + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest), + }, + } + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("Assign", mock.Anything, tc.token, tc.connection.ChannelID, auth.GroupRelation, auth.ThingsKind, []string{tc.connection.ThingID}).Return(tc.svcErr) + err := mgsdk.Connect(tc.connection, tc.token) + assert.Equal(t, tc.err, err) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "Assign", mock.Anything, tc.token, tc.connection.ChannelID, auth.GroupRelation, auth.ThingsKind, []string{tc.connection.ThingID}) + assert.True(t, ok) + } + svcCall.Unset() + }) + } +} + +func TestDisconnect(t *testing.T) { + ts, gsvc := setupChannels() + defer ts.Close() + + conf := sdk.Config{ + ThingsURL: ts.URL, + } + mgsdk := sdk.NewSDK(conf) + + thingID := generateUUID(t) + + cases := []struct { + desc string + token string + disconnect sdk.Connection + svcErr error + err errors.SDKError + }{ + { + desc: "disconnect successfully", + token: validToken, + disconnect: sdk.Connection{ + ChannelID: channel.ID, + ThingID: thingID, + }, + svcErr: nil, + err: nil, + }, + { + desc: "disconnect with invalid token", + token: invalidToken, + disconnect: sdk.Connection{ + ChannelID: channel.ID, + ThingID: thingID, + }, + svcErr: svcerr.ErrAuthentication, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "disconnect with empty token", + token: "", + disconnect: sdk.Connection{ + ChannelID: channel.ID, + ThingID: thingID, + }, + svcErr: svcerr.ErrAuthentication, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "disconnect with invalid channel id", + token: validToken, + disconnect: sdk.Connection{ + ChannelID: wrongID, + ThingID: thingID, + }, + svcErr: svcerr.ErrAuthorization, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + }, + { + desc: "disconnect with empty channel id", + token: validToken, + disconnect: sdk.Connection{ + ChannelID: "", + ThingID: thingID, + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest), + }, + { + desc: "disconnect with empty thing id", + token: validToken, + disconnect: sdk.Connection{ + ChannelID: channel.ID, + ThingID: "", + }, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest), + }, + } + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("Unassign", mock.Anything, tc.token, tc.disconnect.ChannelID, auth.GroupRelation, auth.ThingsKind, []string{tc.disconnect.ThingID}).Return(tc.svcErr) + err := mgsdk.Disconnect(tc.disconnect, tc.token) + assert.Equal(t, tc.err, err) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "Unassign", mock.Anything, tc.token, tc.disconnect.ChannelID, auth.GroupRelation, auth.ThingsKind, []string{tc.disconnect.ThingID}) + assert.True(t, ok) + } + svcCall.Unset() + }) + } +} + +func TestConnectThing(t *testing.T) { + ts, gsvc := setupChannels() + defer ts.Close() + + conf := sdk.Config{ + ThingsURL: ts.URL, + } + mgsdk := sdk.NewSDK(conf) + + thingID := generateUUID(t) + + cases := []struct { + desc string + token string + channelID string + thingID string + svcErr error + err errors.SDKError + }{ + { + desc: "connect successfully", + token: validToken, + channelID: channel.ID, + thingID: thingID, + svcErr: nil, + err: nil, + }, + { + desc: "connect with invalid token", + token: invalidToken, + channelID: channel.ID, + thingID: thingID, + svcErr: svcerr.ErrAuthentication, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "connect with empty token", + token: "", + channelID: channel.ID, + thingID: thingID, + svcErr: svcerr.ErrAuthentication, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "connect with invalid channel id", + token: validToken, + channelID: wrongID, + thingID: thingID, + svcErr: svcerr.ErrAuthorization, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + }, + { + desc: "connect with empty channel id", + token: validToken, + channelID: "", + thingID: thingID, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest), + }, + { + desc: "connect with empty thing id", + token: validToken, + channelID: channel.ID, + thingID: "", + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest), + }, + } + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("Assign", mock.Anything, tc.token, tc.channelID, auth.GroupRelation, auth.ThingsKind, []string{tc.thingID}).Return(tc.svcErr) + err := mgsdk.ConnectThing(tc.thingID, tc.channelID, tc.token) + assert.Equal(t, tc.err, err) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "Assign", mock.Anything, tc.token, tc.channelID, auth.GroupRelation, auth.ThingsKind, []string{tc.thingID}) + assert.True(t, ok) + } + svcCall.Unset() + }) + } +} + +func TestDisconnectThing(t *testing.T) { + ts, gsvc := setupChannels() + defer ts.Close() + + conf := sdk.Config{ + ThingsURL: ts.URL, + } + mgsdk := sdk.NewSDK(conf) + + thingID := generateUUID(t) + + cases := []struct { + desc string + token string + channelID string + thingID string + svcErr error + err errors.SDKError + }{ + { + desc: "disconnect successfully", + token: validToken, + channelID: channel.ID, + thingID: thingID, + svcErr: nil, + err: nil, + }, + { + desc: "disconnect with invalid token", + token: invalidToken, + channelID: channel.ID, + thingID: thingID, + svcErr: svcerr.ErrAuthentication, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "disconnect with empty token", + token: "", + channelID: channel.ID, + thingID: thingID, + svcErr: svcerr.ErrAuthentication, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized), + }, + { + desc: "disconnect with invalid channel id", + token: validToken, + channelID: wrongID, + thingID: thingID, + svcErr: svcerr.ErrAuthorization, + err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + }, + { + desc: "disconnect with empty channel id", + token: validToken, + channelID: "", + thingID: thingID, + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest), + }, + { + desc: "disconnect with empty thing id", + token: validToken, + channelID: channel.ID, + thingID: "", + svcErr: nil, + err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest), + }, + } + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + svcCall := gsvc.On("Unassign", mock.Anything, tc.token, tc.channelID, auth.GroupRelation, auth.ThingsKind, []string{tc.thingID}).Return(tc.svcErr) + err := mgsdk.DisconnectThing(tc.thingID, tc.channelID, tc.token) + assert.Equal(t, tc.err, err) + if tc.err == nil { + ok := svcCall.Parent.AssertCalled(t, "Unassign", mock.Anything, tc.token, tc.channelID, auth.GroupRelation, auth.ThingsKind, []string{tc.thingID}) + assert.True(t, ok) + } + svcCall.Unset() + }) + } +} + +func toIDs(objects interface{}) []string { + v := reflect.ValueOf(objects) + if v.Kind() != reflect.Slice { + panic("objects argument must be a slice") + } + ids := make([]string, v.Len()) + for i := 0; i < v.Len(); i++ { + id := v.Index(i).FieldByName("ID").String() + ids[i] = id + } + + return ids +} + +func generateTestChannel(t *testing.T) sdk.Channel { + createdAt, err := time.Parse(time.RFC3339, "2023-03-03T00:00:00Z") + assert.Nil(t, err, fmt.Sprintf("unexpected error %s", err)) + updatedAt := createdAt + ch := sdk.Channel{ + ID: testsutil.GenerateUUID(&testing.T{}), + DomainID: testsutil.GenerateUUID(&testing.T{}), + Name: channelName, + Description: description, + Metadata: sdk.Metadata{"role": "client"}, + CreatedAt: createdAt, + UpdatedAt: updatedAt, + Status: mgclients.EnabledStatus.String(), + } + return ch } diff --git a/pkg/sdk/go/responses.go b/pkg/sdk/go/responses.go index 7181b49671..480b3ba7cf 100644 --- a/pkg/sdk/go/responses.go +++ b/pkg/sdk/go/responses.go @@ -13,7 +13,7 @@ type createThingsRes struct { Things []Thing `json:"things"` } -type pageRes struct { +type PageRes struct { Total uint64 `json:"total"` Offset uint64 `json:"offset"` Limit uint64 `json:"limit"` @@ -22,40 +22,40 @@ type pageRes struct { // ThingsPage contains list of things in a page with proper metadata. type ThingsPage struct { Things []Thing `json:"things"` - pageRes + PageRes } // ChannelsPage contains list of channels in a page with proper metadata. type ChannelsPage struct { Channels []Channel `json:"groups"` - pageRes + PageRes } // MessagesPage contains list of messages in a page with proper metadata. type MessagesPage struct { Messages []senml.Message `json:"messages,omitempty"` - pageRes + PageRes } type GroupsPage struct { Groups []Group `json:"groups"` - pageRes + PageRes } type UsersPage struct { Users []User `json:"users"` - pageRes + PageRes } type MembersPage struct { Members []User `json:"members"` - pageRes + PageRes } // MembershipsPage contains page related metadata as well as list of memberships that // belong to this page. type MembershipsPage struct { - pageRes + PageRes Memberships []Group `json:"memberships"` } @@ -66,17 +66,17 @@ type revokeCertsRes struct { // bootstrapsPage contains list of bootstrap configs in a page with proper metadata. type BootstrapPage struct { Configs []BootstrapConfig `json:"configs"` - pageRes + PageRes } type CertSerials struct { Certs []Cert `json:"certs"` - pageRes + PageRes } type SubscriptionPage struct { Subscriptions []Subscription `json:"subscriptions"` - pageRes + PageRes } type identifyThingResp struct { @@ -85,5 +85,5 @@ type identifyThingResp struct { type DomainsPage struct { Domains []Domain `json:"domains"` - pageRes + PageRes }