influxdb/tenant/http_handler_urm_test.go

495 lines
14 KiB
Go

package tenant_test
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/go-chi/chi"
"github.com/google/go-cmp/cmp"
"github.com/influxdata/influxdb/v2"
ihttp "github.com/influxdata/influxdb/v2/http"
"github.com/influxdata/influxdb/v2/kit/platform"
"github.com/influxdata/influxdb/v2/mock"
"github.com/influxdata/influxdb/v2/tenant"
itesting "github.com/influxdata/influxdb/v2/testing"
"go.uber.org/zap/zaptest"
)
func TestUserResourceMappingService_GetMembersHandler(t *testing.T) {
type fields struct {
userService influxdb.UserService
userResourceMappingService influxdb.UserResourceMappingService
}
type args struct {
resourceID string
userType influxdb.UserType
}
type wants struct {
statusCode int
contentType string
body string
}
tests := []struct {
name string
fields fields
args args
wants wants
}{
{
name: "get members",
fields: fields{
userService: &mock.UserService{
FindUserByIDFn: func(ctx context.Context, id platform.ID) (*influxdb.User, error) {
return &influxdb.User{ID: id, Name: fmt.Sprintf("user%s", id), Status: influxdb.Active}, nil
},
},
userResourceMappingService: &mock.UserResourceMappingService{
FindMappingsFn: func(ctx context.Context, filter influxdb.UserResourceMappingFilter) ([]*influxdb.UserResourceMapping, int, error) {
ms := []*influxdb.UserResourceMapping{
{
ResourceID: filter.ResourceID,
ResourceType: filter.ResourceType,
UserType: filter.UserType,
UserID: 1,
},
{
ResourceID: filter.ResourceID,
ResourceType: filter.ResourceType,
UserType: filter.UserType,
UserID: 2,
},
}
return ms, len(ms), nil
},
},
},
args: args{
resourceID: "0000000000000099",
userType: influxdb.Member,
},
wants: wants{
statusCode: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: `{
"links": {
"self": "/api/v2/%s/0000000000000099/members"
},
"users": [
{
"role": "member",
"links": {
"self": "/api/v2/users/0000000000000001"
},
"id": "0000000000000001",
"name": "user0000000000000001",
"status": "active"
},
{
"role": "member",
"links": {
"self": "/api/v2/users/0000000000000002"
},
"id": "0000000000000002",
"name": "user0000000000000002",
"status": "active"
}
]
}`,
},
},
{
name: "get owners",
fields: fields{
userService: &mock.UserService{
FindUserByIDFn: func(ctx context.Context, id platform.ID) (*influxdb.User, error) {
return &influxdb.User{ID: id, Name: fmt.Sprintf("user%s", id), Status: influxdb.Active}, nil
},
},
userResourceMappingService: &mock.UserResourceMappingService{
FindMappingsFn: func(ctx context.Context, filter influxdb.UserResourceMappingFilter) ([]*influxdb.UserResourceMapping, int, error) {
ms := []*influxdb.UserResourceMapping{
{
ResourceID: filter.ResourceID,
ResourceType: filter.ResourceType,
UserType: filter.UserType,
UserID: 1,
},
{
ResourceID: filter.ResourceID,
ResourceType: filter.ResourceType,
UserType: filter.UserType,
UserID: 2,
},
}
return ms, len(ms), nil
},
},
},
args: args{
resourceID: "0000000000000099",
userType: influxdb.Owner,
},
wants: wants{
statusCode: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: `{
"links": {
"self": "/api/v2/%s/0000000000000099/owners"
},
"users": [
{
"role": "owner",
"links": {
"self": "/api/v2/users/0000000000000001"
},
"id": "0000000000000001",
"name": "user0000000000000001",
"status": "active"
},
{
"role": "owner",
"links": {
"self": "/api/v2/users/0000000000000002"
},
"id": "0000000000000002",
"name": "user0000000000000002",
"status": "active"
}
]
}`,
},
},
}
for _, tt := range tests {
resourceTypes := []influxdb.ResourceType{
influxdb.BucketsResourceType,
influxdb.DashboardsResourceType,
influxdb.OrgsResourceType,
influxdb.SourcesResourceType,
influxdb.TasksResourceType,
influxdb.TelegrafsResourceType,
influxdb.UsersResourceType,
}
for _, resourceType := range resourceTypes {
t.Run(tt.name+"_"+string(resourceType), func(t *testing.T) {
// create server
h := tenant.NewURMHandler(zaptest.NewLogger(t), resourceType, "id", tt.fields.userService, tt.fields.userResourceMappingService)
router := chi.NewRouter()
router.Mount(fmt.Sprintf("/api/v2/%s/{id}/members", resourceType), h)
router.Mount(fmt.Sprintf("/api/v2/%s/{id}/owners", resourceType), h)
s := httptest.NewServer(router)
defer s.Close()
// craft request
r, err := http.NewRequest("GET", fmt.Sprintf("%s/api/v2/%s/%s/%ss", s.URL, resourceType, tt.args.resourceID, tt.args.userType), nil)
if err != nil {
t.Fatal(err)
}
c := s.Client()
res, err := c.Do(r)
if err != nil {
t.Fatal(err)
}
// check response
content := res.Header.Get("Content-Type")
body, _ := io.ReadAll(res.Body)
if res.StatusCode != tt.wants.statusCode {
t.Errorf("%q. GetMembersHandler() = %v, want %v", tt.name, res.StatusCode, tt.wants.statusCode)
}
if tt.wants.contentType != "" && content != tt.wants.contentType {
t.Errorf("%q. GetMembersHandler() = %v, want %v", tt.name, content, tt.wants.contentType)
}
if diff := cmp.Diff(string(body), fmt.Sprintf(tt.wants.body, resourceType)); tt.wants.body != "" && diff != "" {
t.Errorf("%q. GetMembersHandler() = ***%s***", tt.name, diff)
}
})
}
}
}
func TestUserResourceMappingService_PostMembersHandler(t *testing.T) {
type fields struct {
userService influxdb.UserService
userResourceMappingService influxdb.UserResourceMappingService
}
type args struct {
resourceID string
userType influxdb.UserType
user influxdb.User
}
type wants struct {
statusCode int
contentType string
body string
}
tests := []struct {
name string
fields fields
args args
wants wants
}{
{
name: "post members",
fields: fields{
userService: &mock.UserService{
FindUserByIDFn: func(ctx context.Context, id platform.ID) (*influxdb.User, error) {
return &influxdb.User{ID: id, Name: fmt.Sprintf("user%s", id), Status: influxdb.Active}, nil
},
},
userResourceMappingService: &mock.UserResourceMappingService{
CreateMappingFn: func(ctx context.Context, m *influxdb.UserResourceMapping) error {
return nil
},
},
},
args: args{
resourceID: "0000000000000099",
user: influxdb.User{
ID: 1,
Name: "user0000000000000001",
Status: influxdb.Active,
},
userType: influxdb.Member,
},
wants: wants{
statusCode: http.StatusCreated,
contentType: "application/json; charset=utf-8",
body: `{
"role": "member",
"links": {
"self": "/api/v2/users/0000000000000001"
},
"id": "0000000000000001",
"name": "user0000000000000001",
"status": "active"
}`,
},
},
{
name: "post owners",
fields: fields{
userService: &mock.UserService{
FindUserByIDFn: func(ctx context.Context, id platform.ID) (*influxdb.User, error) {
return &influxdb.User{ID: id, Name: fmt.Sprintf("user%s", id), Status: influxdb.Active}, nil
},
},
userResourceMappingService: &mock.UserResourceMappingService{
CreateMappingFn: func(ctx context.Context, m *influxdb.UserResourceMapping) error {
return nil
},
},
},
args: args{
resourceID: "0000000000000099",
user: influxdb.User{
ID: 2,
Name: "user0000000000000002",
Status: influxdb.Active,
},
userType: influxdb.Owner,
},
wants: wants{
statusCode: http.StatusCreated,
contentType: "application/json; charset=utf-8",
body: `{
"role": "owner",
"links": {
"self": "/api/v2/users/0000000000000002"
},
"id": "0000000000000002",
"name": "user0000000000000002",
"status": "active"
}`,
},
},
}
for _, tt := range tests {
resourceTypes := []influxdb.ResourceType{
influxdb.BucketsResourceType,
influxdb.DashboardsResourceType,
influxdb.OrgsResourceType,
influxdb.SourcesResourceType,
influxdb.TasksResourceType,
influxdb.TelegrafsResourceType,
influxdb.UsersResourceType,
}
for _, resourceType := range resourceTypes {
t.Run(tt.name+"_"+string(resourceType), func(t *testing.T) {
// create server
h := tenant.NewURMHandler(zaptest.NewLogger(t), resourceType, "id", tt.fields.userService, tt.fields.userResourceMappingService)
router := chi.NewRouter()
router.Mount(fmt.Sprintf("/api/v2/%s/{id}/members", resourceType), h)
router.Mount(fmt.Sprintf("/api/v2/%s/{id}/owners", resourceType), h)
s := httptest.NewServer(router)
defer s.Close()
// craft request
b, err := json.Marshal(tt.args.user)
if err != nil {
t.Fatalf("failed to unmarshal user: %v", err)
}
r, err := http.NewRequest("POST", fmt.Sprintf("%s/api/v2/%s/%s/%ss", s.URL, resourceType, tt.args.resourceID, tt.args.userType), bytes.NewReader(b))
if err != nil {
t.Fatal(err)
}
c := s.Client()
res, err := c.Do(r)
if err != nil {
t.Fatal(err)
}
content := res.Header.Get("Content-Type")
body, _ := io.ReadAll(res.Body)
if res.StatusCode != tt.wants.statusCode {
t.Errorf("%q. PostMembersHandler() = %v, want %v", tt.name, res.StatusCode, tt.wants.statusCode)
}
if tt.wants.contentType != "" && content != tt.wants.contentType {
t.Errorf("%q. PostMembersHandler() = %v, want %v", tt.name, content, tt.wants.contentType)
}
if tt.wants.body != "" {
if diff := cmp.Diff(string(body), tt.wants.body); diff != "" {
t.Errorf("%q. PostMembersHandler() = ***%s***", tt.name, diff)
}
}
})
}
}
}
func TestUserResourceMappingService_Client(t *testing.T) {
type fields struct {
userService influxdb.UserService
userResourceMappingService influxdb.UserResourceMappingService
}
type args struct {
resourceID string
userType influxdb.UserType
user influxdb.User
}
tests := []struct {
name string
fields fields
args args
}{
{
name: "post members",
fields: fields{
userService: &mock.UserService{
FindUserByIDFn: func(ctx context.Context, id platform.ID) (*influxdb.User, error) {
return &influxdb.User{ID: id, Name: fmt.Sprintf("user%s", id), Status: influxdb.Active}, nil
},
},
userResourceMappingService: &mock.UserResourceMappingService{
CreateMappingFn: func(ctx context.Context, m *influxdb.UserResourceMapping) error {
return nil
},
FindMappingsFn: func(ctx context.Context, f influxdb.UserResourceMappingFilter) ([]*influxdb.UserResourceMapping, int, error) {
return []*influxdb.UserResourceMapping{&influxdb.UserResourceMapping{}}, 1, nil
},
},
},
args: args{
resourceID: "0000000000000099",
user: influxdb.User{
ID: 1,
Name: "user0000000000000001",
Status: influxdb.Active,
},
userType: influxdb.Member,
},
},
{
name: "post owners",
fields: fields{
userService: &mock.UserService{
FindUserByIDFn: func(ctx context.Context, id platform.ID) (*influxdb.User, error) {
return &influxdb.User{ID: id, Name: fmt.Sprintf("user%s", id), Status: influxdb.Active}, nil
},
},
userResourceMappingService: &mock.UserResourceMappingService{
CreateMappingFn: func(ctx context.Context, m *influxdb.UserResourceMapping) error {
return nil
},
FindMappingsFn: func(ctx context.Context, f influxdb.UserResourceMappingFilter) ([]*influxdb.UserResourceMapping, int, error) {
return []*influxdb.UserResourceMapping{&influxdb.UserResourceMapping{}}, 1, nil
},
},
},
args: args{
resourceID: "0000000000000099",
user: influxdb.User{
ID: 2,
Name: "user0000000000000002",
Status: influxdb.Active,
},
userType: influxdb.Owner,
},
},
}
for _, tt := range tests {
resourceTypes := []influxdb.ResourceType{
influxdb.BucketsResourceType,
influxdb.DashboardsResourceType,
influxdb.OrgsResourceType,
influxdb.SourcesResourceType,
influxdb.TasksResourceType,
influxdb.TelegrafsResourceType,
influxdb.UsersResourceType,
}
for _, resourceType := range resourceTypes {
t.Run(tt.name+"_"+string(resourceType), func(t *testing.T) {
// create server
h := tenant.NewURMHandler(zaptest.NewLogger(t), resourceType, "id", tt.fields.userService, tt.fields.userResourceMappingService)
router := chi.NewRouter()
router.Mount(fmt.Sprintf("/api/v2/%s/{id}/members", resourceType), h)
router.Mount(fmt.Sprintf("/api/v2/%s/{id}/owners", resourceType), h)
s := httptest.NewServer(router)
defer s.Close()
ctx := context.Background()
resourceID := itesting.MustIDBase16(tt.args.resourceID)
urm := &influxdb.UserResourceMapping{ResourceType: resourceType, ResourceID: resourceID, UserType: tt.args.userType, UserID: tt.args.user.ID}
httpClient, err := ihttp.NewHTTPClient(s.URL, "", false)
if err != nil {
t.Fatal(err)
}
c := tenant.UserResourceMappingClient{Client: httpClient}
err = c.CreateUserResourceMapping(ctx, urm)
if err != nil {
t.Fatal(err)
}
_, n, err := c.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{ResourceID: resourceID, ResourceType: resourceType, UserType: tt.args.userType})
if err != nil {
t.Fatal(err)
}
if n != 1 {
t.Fatalf("expected 1 urm to be created, got: %d", n)
}
})
}
}
}