Files
influx-cli/clients/org/org_members_test.go
Daniel Moran 1dad2f5f72 refactor: move things around to make building a client easier from other projects (#123)
* refactor: split logic for building API config into public func
* refactor: move config code out of internal/
2021-06-16 11:09:26 -04:00

453 lines
17 KiB
Go

package org_test
import (
"bytes"
"context"
"errors"
"fmt"
"strings"
"testing"
"github.com/golang/mock/gomock"
"github.com/influxdata/influx-cli/v2/api"
"github.com/influxdata/influx-cli/v2/clients"
"github.com/influxdata/influx-cli/v2/clients/org"
"github.com/influxdata/influx-cli/v2/config"
"github.com/influxdata/influx-cli/v2/internal/mock"
"github.com/influxdata/influx-cli/v2/internal/testutils"
"github.com/influxdata/influx-cli/v2/pkg/influxid"
"github.com/stretchr/testify/assert"
tmock "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
var id1, _ = influxid.IDFromString("1111111111111111")
var id2, _ = influxid.IDFromString("2222222222222222")
func TestClient_AddMember(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
params org.AddMemberParams
defaultOrgName string
registerExpectations func(*testing.T, *mock.MockOrganizationsApi)
expectedOut string
expectedErr string
}{
{
name: "org by ID",
params: org.AddMemberParams{
OrgID: id1,
MemberId: id2,
},
defaultOrgName: "my-org",
registerExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().PostOrgsIDMembers(gomock.Any(), gomock.Eq(id1.String())).
Return(api.ApiPostOrgsIDMembersRequest{ApiService: orgApi}.OrgID(id1.String()))
orgApi.EXPECT().PostOrgsIDMembersExecute(tmock.MatchedBy(func(in api.ApiPostOrgsIDMembersRequest) bool {
body := in.GetAddResourceMemberRequestBody()
return assert.Equal(t, id1.String(), in.GetOrgID()) &&
assert.NotNil(t, body) &&
assert.Equal(t, id2.String(), body.GetId())
})).Return(api.ResourceMember{Id: api.PtrString(id2.String())}, nil)
},
expectedOut: "user \"2222222222222222\" has been added as a member of org \"1111111111111111\"",
},
{
name: "org by name",
params: org.AddMemberParams{
OrgName: "org",
MemberId: id2,
},
defaultOrgName: "my-org",
registerExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().GetOrgs(gomock.Any()).Return(api.ApiGetOrgsRequest{ApiService: orgApi})
orgApi.EXPECT().GetOrgsExecute(tmock.MatchedBy(func(in api.ApiGetOrgsRequest) bool {
return assert.Equal(t, "org", *in.GetOrg())
})).Return(api.Organizations{
Orgs: &[]api.Organization{{Id: api.PtrString(id1.String())}},
}, nil)
orgApi.EXPECT().PostOrgsIDMembers(gomock.Any(), gomock.Eq(id1.String())).
Return(api.ApiPostOrgsIDMembersRequest{ApiService: orgApi}.OrgID(id1.String()))
orgApi.EXPECT().PostOrgsIDMembersExecute(tmock.MatchedBy(func(in api.ApiPostOrgsIDMembersRequest) bool {
body := in.GetAddResourceMemberRequestBody()
return assert.Equal(t, id1.String(), in.GetOrgID()) &&
assert.NotNil(t, body) &&
assert.Equal(t, id2.String(), body.GetId())
})).Return(api.ResourceMember{Id: api.PtrString(id2.String())}, nil)
},
expectedOut: "user \"2222222222222222\" has been added as a member of org \"1111111111111111\"",
},
{
name: "by config org",
params: org.AddMemberParams{
MemberId: id2,
},
defaultOrgName: "my-org",
registerExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().GetOrgs(gomock.Any()).Return(api.ApiGetOrgsRequest{ApiService: orgApi})
orgApi.EXPECT().GetOrgsExecute(tmock.MatchedBy(func(in api.ApiGetOrgsRequest) bool {
return assert.Equal(t, "my-org", *in.GetOrg())
})).Return(api.Organizations{
Orgs: &[]api.Organization{{Id: api.PtrString(id1.String())}},
}, nil)
orgApi.EXPECT().PostOrgsIDMembers(gomock.Any(), gomock.Eq(id1.String())).
Return(api.ApiPostOrgsIDMembersRequest{ApiService: orgApi}.OrgID(id1.String()))
orgApi.EXPECT().PostOrgsIDMembersExecute(tmock.MatchedBy(func(in api.ApiPostOrgsIDMembersRequest) bool {
body := in.GetAddResourceMemberRequestBody()
return assert.Equal(t, id1.String(), in.GetOrgID()) &&
assert.NotNil(t, body) &&
assert.Equal(t, id2.String(), body.GetId())
})).Return(api.ResourceMember{Id: api.PtrString(id2.String())}, nil)
},
expectedOut: "user \"2222222222222222\" has been added as a member of org \"1111111111111111\"",
},
{
name: "no such org",
params: org.AddMemberParams{
MemberId: id2,
},
defaultOrgName: "my-org",
registerExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().GetOrgs(gomock.Any()).Return(api.ApiGetOrgsRequest{ApiService: orgApi})
orgApi.EXPECT().GetOrgsExecute(tmock.MatchedBy(func(in api.ApiGetOrgsRequest) bool {
return assert.Equal(t, "my-org", *in.GetOrg())
})).Return(api.Organizations{}, errors.New("not found"))
},
expectedErr: "not found",
},
{
name: "missing org",
expectedErr: clients.ErrMustSpecifyOrg.Error(),
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
api := mock.NewMockOrganizationsApi(ctrl)
if tc.registerExpectations != nil {
tc.registerExpectations(t, api)
}
stdout := bytes.Buffer{}
stdio := mock.NewMockStdIO(ctrl)
stdio.EXPECT().Write(gomock.Any()).DoAndReturn(stdout.Write).AnyTimes()
cli := org.Client{
CLI: clients.CLI{StdIO: stdio, ActiveConfig: config.Config{Org: tc.defaultOrgName}},
OrganizationsApi: api,
}
err := cli.AddMember(context.Background(), &tc.params)
if tc.expectedErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
return
}
require.NoError(t, err)
require.Equal(t, tc.expectedOut, strings.TrimSpace(stdout.String()))
})
}
}
func TestClient_ListMembers(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
params org.ListMemberParams
defaultOrgName string
registerOrgExpectations func(*testing.T, *mock.MockOrganizationsApi)
registerUserExpectations func(*testing.T, *mock.MockUsersApi)
expectedOut []string
expectedErr string
}{
{
name: "no members",
defaultOrgName: "my-org",
registerOrgExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().GetOrgs(gomock.Any()).Return(api.ApiGetOrgsRequest{ApiService: orgApi})
orgApi.EXPECT().GetOrgsExecute(tmock.MatchedBy(func(in api.ApiGetOrgsRequest) bool {
return assert.Equal(t, "my-org", *in.GetOrg())
})).Return(api.Organizations{
Orgs: &[]api.Organization{{Id: api.PtrString(id1.String())}},
}, nil)
req := api.ApiGetOrgsIDMembersRequest{ApiService: orgApi}.OrgID(id1.String())
orgApi.EXPECT().GetOrgsIDMembers(gomock.Any(), gomock.Eq(id1.String())).Return(req)
orgApi.EXPECT().GetOrgsIDMembersExecute(gomock.Eq(req)).Return(api.ResourceMembers{}, nil)
},
},
{
name: "one member",
params: org.ListMemberParams{
OrgName: "org",
},
defaultOrgName: "my-org",
registerOrgExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().GetOrgs(gomock.Any()).Return(api.ApiGetOrgsRequest{ApiService: orgApi})
orgApi.EXPECT().GetOrgsExecute(tmock.MatchedBy(func(in api.ApiGetOrgsRequest) bool {
return assert.Equal(t, "org", *in.GetOrg())
})).Return(api.Organizations{
Orgs: &[]api.Organization{{Id: api.PtrString(id1.String())}},
}, nil)
req := api.ApiGetOrgsIDMembersRequest{ApiService: orgApi}.OrgID(id1.String())
orgApi.EXPECT().GetOrgsIDMembers(gomock.Any(), gomock.Eq(id1.String())).Return(req)
orgApi.EXPECT().GetOrgsIDMembersExecute(gomock.Eq(req)).
Return(api.ResourceMembers{Users: &[]api.ResourceMember{{Id: api.PtrString(id2.String())}}}, nil)
},
registerUserExpectations: func(t *testing.T, userApi *mock.MockUsersApi) {
req := api.ApiGetUsersIDRequest{ApiService: userApi}.UserID(id2.String())
userApi.EXPECT().GetUsersID(gomock.Any(), gomock.Eq(id2.String())).Return(req)
userApi.EXPECT().GetUsersIDExecute(gomock.Eq(req)).Return(api.UserResponse{
Id: api.PtrString(id2.String()),
Name: "user1",
Status: api.PtrString("active"),
}, nil)
},
expectedOut: []string{`2222222222222222\s+user1\s+member\s+active`},
},
{
name: "many members",
params: org.ListMemberParams{
OrgID: id1,
},
// NOTE: We previously saw a deadlock when # members was > 10, so test that here.
registerOrgExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
req := api.ApiGetOrgsIDMembersRequest{ApiService: orgApi}.OrgID(id1.String())
orgApi.EXPECT().GetOrgsIDMembers(gomock.Any(), gomock.Eq(id1.String())).Return(req)
members := make([]api.ResourceMember, 11)
for i := 0; i < 11; i++ {
members[i] = api.ResourceMember{Id: api.PtrString(fmt.Sprintf("%016d", i))}
}
orgApi.EXPECT().GetOrgsIDMembersExecute(gomock.Eq(req)).Return(api.ResourceMembers{Users: &members}, nil)
},
registerUserExpectations: func(t *testing.T, userApi *mock.MockUsersApi) {
for i := 0; i < 11; i++ {
id := fmt.Sprintf("%016d", i)
status := "active"
if i%2 == 0 {
status = "inactive"
}
req := api.ApiGetUsersIDRequest{ApiService: userApi}.UserID(id)
userApi.EXPECT().GetUsersID(gomock.Any(), gomock.Eq(id)).Return(req)
userApi.EXPECT().GetUsersIDExecute(gomock.Eq(req)).Return(api.UserResponse{
Id: &id,
Name: fmt.Sprintf("user%d", i),
Status: &status,
}, nil)
}
},
expectedOut: []string{
`0000000000000000\s+user0\s+member\s+inactive`,
`0000000000000001\s+user1\s+member\s+active`,
`0000000000000002\s+user2\s+member\s+inactive`,
`0000000000000003\s+user3\s+member\s+active`,
`0000000000000004\s+user4\s+member\s+inactive`,
`0000000000000005\s+user5\s+member\s+active`,
`0000000000000006\s+user6\s+member\s+inactive`,
`0000000000000007\s+user7\s+member\s+active`,
`0000000000000008\s+user8\s+member\s+inactive`,
`0000000000000009\s+user9\s+member\s+active`,
`0000000000000010\s+user10\s+member\s+inactive`,
},
},
{
name: "no such org",
defaultOrgName: "my-org",
registerOrgExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().GetOrgs(gomock.Any()).Return(api.ApiGetOrgsRequest{ApiService: orgApi})
orgApi.EXPECT().GetOrgsExecute(tmock.MatchedBy(func(in api.ApiGetOrgsRequest) bool {
return assert.Equal(t, "my-org", *in.GetOrg())
})).Return(api.Organizations{}, errors.New("not found"))
},
expectedErr: "not found",
},
{
name: "no such user",
params: org.ListMemberParams{
OrgID: id1,
},
registerOrgExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
req := api.ApiGetOrgsIDMembersRequest{ApiService: orgApi}.OrgID(id1.String())
orgApi.EXPECT().GetOrgsIDMembers(gomock.Any(), gomock.Eq(id1.String())).Return(req)
orgApi.EXPECT().GetOrgsIDMembersExecute(gomock.Eq(req)).
Return(api.ResourceMembers{Users: &[]api.ResourceMember{{Id: api.PtrString(id2.String())}}}, nil)
},
registerUserExpectations: func(t *testing.T, userApi *mock.MockUsersApi) {
req := api.ApiGetUsersIDRequest{ApiService: userApi}.UserID(id2.String())
userApi.EXPECT().GetUsersID(gomock.Any(), gomock.Eq(id2.String())).Return(req)
userApi.EXPECT().GetUsersIDExecute(gomock.Eq(req)).Return(api.UserResponse{}, errors.New("not found"))
},
expectedErr: "user \"2222222222222222\": not found",
},
{
name: "missing org",
expectedErr: clients.ErrMustSpecifyOrg.Error(),
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
orgApi := mock.NewMockOrganizationsApi(ctrl)
userApi := mock.NewMockUsersApi(ctrl)
if tc.registerOrgExpectations != nil {
tc.registerOrgExpectations(t, orgApi)
}
if tc.registerUserExpectations != nil {
tc.registerUserExpectations(t, userApi)
}
stdout := bytes.Buffer{}
stdio := mock.NewMockStdIO(ctrl)
stdio.EXPECT().Write(gomock.Any()).DoAndReturn(stdout.Write).AnyTimes()
cli := org.Client{
CLI: clients.CLI{StdIO: stdio, ActiveConfig: config.Config{Org: tc.defaultOrgName}},
OrganizationsApi: orgApi,
UsersApi: userApi,
}
err := cli.ListMembers(context.Background(), &tc.params)
if tc.expectedErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
return
}
require.NoError(t, err)
testutils.MatchLines(t, append([]string{`ID\s+Name\s+User Type\s+Status`}, tc.expectedOut...),
strings.Split(stdout.String(), "\n"))
})
}
}
func TestClient_RemoveMembers(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
params org.RemoveMemberParams
defaultOrgName string
registerExpectations func(*testing.T, *mock.MockOrganizationsApi)
expectedOut string
expectedErr string
}{
{
name: "org by ID",
params: org.RemoveMemberParams{
OrgID: id1,
MemberId: id2,
},
defaultOrgName: "my-org",
registerExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
req := api.ApiDeleteOrgsIDMembersIDRequest{ApiService: orgApi}.OrgID(id1.String()).UserID(id2.String())
orgApi.EXPECT().
DeleteOrgsIDMembersID(gomock.Any(), gomock.Eq(id1.String()), gomock.Eq(id2.String())).Return(req)
orgApi.EXPECT().DeleteOrgsIDMembersIDExecute(gomock.Eq(req)).Return(nil)
},
expectedOut: "user \"2222222222222222\" has been removed from org \"1111111111111111\"",
},
{
name: "org by name",
params: org.RemoveMemberParams{
OrgName: "org",
MemberId: id2,
},
defaultOrgName: "my-org",
registerExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().GetOrgs(gomock.Any()).Return(api.ApiGetOrgsRequest{ApiService: orgApi})
orgApi.EXPECT().GetOrgsExecute(tmock.MatchedBy(func(in api.ApiGetOrgsRequest) bool {
return assert.Equal(t, "org", *in.GetOrg())
})).Return(api.Organizations{
Orgs: &[]api.Organization{{Id: api.PtrString(id1.String())}},
}, nil)
req := api.ApiDeleteOrgsIDMembersIDRequest{ApiService: orgApi}.OrgID(id1.String()).UserID(id2.String())
orgApi.EXPECT().
DeleteOrgsIDMembersID(gomock.Any(), gomock.Eq(id1.String()), gomock.Eq(id2.String())).Return(req)
orgApi.EXPECT().DeleteOrgsIDMembersIDExecute(gomock.Eq(req)).Return(nil)
},
expectedOut: "user \"2222222222222222\" has been removed from org \"1111111111111111\"",
},
{
name: "by config org",
params: org.RemoveMemberParams{
MemberId: id2,
},
defaultOrgName: "my-org",
registerExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().GetOrgs(gomock.Any()).Return(api.ApiGetOrgsRequest{ApiService: orgApi})
orgApi.EXPECT().GetOrgsExecute(tmock.MatchedBy(func(in api.ApiGetOrgsRequest) bool {
return assert.Equal(t, "my-org", *in.GetOrg())
})).Return(api.Organizations{
Orgs: &[]api.Organization{{Id: api.PtrString(id1.String())}},
}, nil)
req := api.ApiDeleteOrgsIDMembersIDRequest{ApiService: orgApi}.OrgID(id1.String()).UserID(id2.String())
orgApi.EXPECT().
DeleteOrgsIDMembersID(gomock.Any(), gomock.Eq(id1.String()), gomock.Eq(id2.String())).Return(req)
orgApi.EXPECT().DeleteOrgsIDMembersIDExecute(gomock.Eq(req)).Return(nil)
},
expectedOut: "user \"2222222222222222\" has been removed from org \"1111111111111111\"",
},
{
name: "no such org",
params: org.RemoveMemberParams{
MemberId: id2,
},
defaultOrgName: "my-org",
registerExpectations: func(t *testing.T, orgApi *mock.MockOrganizationsApi) {
orgApi.EXPECT().GetOrgs(gomock.Any()).Return(api.ApiGetOrgsRequest{ApiService: orgApi})
orgApi.EXPECT().GetOrgsExecute(tmock.MatchedBy(func(in api.ApiGetOrgsRequest) bool {
return assert.Equal(t, "my-org", *in.GetOrg())
})).Return(api.Organizations{}, errors.New("not found"))
},
expectedErr: "not found",
},
{
name: "missing org",
expectedErr: clients.ErrMustSpecifyOrg.Error(),
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
api := mock.NewMockOrganizationsApi(ctrl)
if tc.registerExpectations != nil {
tc.registerExpectations(t, api)
}
stdout := bytes.Buffer{}
stdio := mock.NewMockStdIO(ctrl)
stdio.EXPECT().Write(gomock.Any()).DoAndReturn(stdout.Write).AnyTimes()
cli := org.Client{
CLI: clients.CLI{StdIO: stdio, ActiveConfig: config.Config{Org: tc.defaultOrgName}},
OrganizationsApi: api,
}
err := cli.RemoveMember(context.Background(), &tc.params)
if tc.expectedErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
return
}
require.NoError(t, err)
require.Equal(t, tc.expectedOut, strings.TrimSpace(stdout.String()))
})
}
}