add testing of org and user processing
This commit is contained in:
4
go.mod
4
go.mod
@@ -9,6 +9,7 @@ require (
|
|||||||
github.com/spf13/cobra v1.7.0
|
github.com/spf13/cobra v1.7.0
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/spf13/viper v1.16.0
|
github.com/spf13/viper v1.16.0
|
||||||
|
github.com/stretchr/testify v1.8.4
|
||||||
go.infratographer.com/x v0.3.3
|
go.infratographer.com/x v0.3.3
|
||||||
go.opentelemetry.io/otel v1.16.0
|
go.opentelemetry.io/otel v1.16.0
|
||||||
go.opentelemetry.io/otel/trace v1.16.0
|
go.opentelemetry.io/otel/trace v1.16.0
|
||||||
@@ -22,6 +23,7 @@ require (
|
|||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
github.com/garsue/watermillzap v1.2.0 // indirect
|
github.com/garsue/watermillzap v1.2.0 // indirect
|
||||||
github.com/go-logr/logr v1.2.4 // indirect
|
github.com/go-logr/logr v1.2.4 // indirect
|
||||||
@@ -52,6 +54,7 @@ require (
|
|||||||
github.com/oklog/ulid v1.3.1 // indirect
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_golang v1.14.0 // indirect
|
github.com/prometheus/client_golang v1.14.0 // indirect
|
||||||
github.com/prometheus/client_model v0.3.0 // indirect
|
github.com/prometheus/client_model v0.3.0 // indirect
|
||||||
github.com/prometheus/common v0.40.0 // indirect
|
github.com/prometheus/common v0.40.0 // indirect
|
||||||
@@ -59,6 +62,7 @@ require (
|
|||||||
github.com/spf13/afero v1.9.5 // indirect
|
github.com/spf13/afero v1.9.5 // indirect
|
||||||
github.com/spf13/cast v1.5.1 // indirect
|
github.com/spf13/cast v1.5.1 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
|
github.com/stretchr/objx v0.5.0 // indirect
|
||||||
github.com/subosito/gotenv v1.4.2 // indirect
|
github.com/subosito/gotenv v1.4.2 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
|
|||||||
1
go.sum
1
go.sum
@@ -280,6 +280,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
||||||
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
|
|||||||
52
internal/metal/providers/mock.go
Normal file
52
internal/metal/providers/mock.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package providers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"go.infratographer.com/x/gidx"
|
||||||
|
|
||||||
|
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ Provider = &MockProvider{}
|
||||||
|
|
||||||
|
// MockProvider implements Provider used for testing.
|
||||||
|
type MockProvider struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrganizationDetails implements Provider used for testing.
|
||||||
|
func (p *MockProvider) GetOrganizationDetails(ctx context.Context, id gidx.PrefixedID) (*models.OrganizationDetails, error) {
|
||||||
|
args := p.Called(id)
|
||||||
|
|
||||||
|
return args.Get(0).(*models.OrganizationDetails), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProjectDetails implements Provider used for testing.
|
||||||
|
func (p *MockProvider) GetProjectDetails(ctx context.Context, id gidx.PrefixedID) (*models.ProjectDetails, error) {
|
||||||
|
args := p.Called(id)
|
||||||
|
|
||||||
|
return args.Get(0).(*models.ProjectDetails), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserDetails implements Provider used for testing.
|
||||||
|
func (p *MockProvider) GetUserDetails(ctx context.Context, id gidx.PrefixedID) (*models.UserDetails, error) {
|
||||||
|
args := p.Called(id)
|
||||||
|
|
||||||
|
return args.Get(0).(*models.UserDetails), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserOrganizationRole implements Provider used for testing.
|
||||||
|
func (p *MockProvider) GetUserOrganizationRole(ctx context.Context, userID, orgID gidx.PrefixedID) (string, error) {
|
||||||
|
args := p.Called(userID, orgID)
|
||||||
|
|
||||||
|
return args.String(0), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserProjectRole implements Provider used for testing.
|
||||||
|
func (p *MockProvider) GetUserProjectRole(ctx context.Context, userID, projID gidx.PrefixedID) (string, error) {
|
||||||
|
args := p.Called(userID, projID)
|
||||||
|
|
||||||
|
return args.String(0), args.Error(1)
|
||||||
|
}
|
||||||
90
internal/permissions/mock.go
Normal file
90
internal/permissions/mock.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"go.infratographer.com/x/gidx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockClient implements Client for testing.
|
||||||
|
type MockClient struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssignRole implements Client for testing.
|
||||||
|
func (c *MockClient) AssignRole(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) error {
|
||||||
|
args := c.Called(roleID, memberID)
|
||||||
|
|
||||||
|
return args.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRole implements Client for testing.
|
||||||
|
func (c *MockClient) CreateRole(ctx context.Context, resourceID gidx.PrefixedID, actions []string) (gidx.PrefixedID, error) {
|
||||||
|
args := c.Called(resourceID, actions)
|
||||||
|
|
||||||
|
return args.Get(0).(gidx.PrefixedID), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResourceRelationship implements Client for testing.
|
||||||
|
func (c *MockClient) DeleteResourceRelationship(ctx context.Context, resourceID gidx.PrefixedID, relation string, relatedResourceID gidx.PrefixedID) error {
|
||||||
|
args := c.Called(resourceID, relation, relatedResourceID)
|
||||||
|
|
||||||
|
return args.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRole implements Client for testing.
|
||||||
|
func (c *MockClient) DeleteRole(ctx context.Context, roleID gidx.PrefixedID) error {
|
||||||
|
args := c.Called(roleID)
|
||||||
|
|
||||||
|
return args.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindResourceRoleByActions implements Client for testing.
|
||||||
|
func (c *MockClient) FindResourceRoleByActions(ctx context.Context, resourceID gidx.PrefixedID, actions []string) (ResourceRole, error) {
|
||||||
|
args := c.Called(resourceID, actions)
|
||||||
|
|
||||||
|
return args.Get(0).(ResourceRole), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListResourceRelationshipsFrom implements Client for testing.
|
||||||
|
func (c *MockClient) ListResourceRelationshipsFrom(ctx context.Context, resourceID gidx.PrefixedID) ([]ResourceRelationship, error) {
|
||||||
|
args := c.Called(resourceID)
|
||||||
|
|
||||||
|
return args.Get(0).([]ResourceRelationship), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListResourceRelationshipsTo implements Client for testing.
|
||||||
|
func (c *MockClient) ListResourceRelationshipsTo(ctx context.Context, resourceID gidx.PrefixedID) ([]ResourceRelationship, error) {
|
||||||
|
args := c.Called(resourceID)
|
||||||
|
|
||||||
|
return args.Get(0).([]ResourceRelationship), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListResourceRoles implements Client for testing.
|
||||||
|
func (c *MockClient) ListResourceRoles(ctx context.Context, resourceID gidx.PrefixedID) (ResourceRoles, error) {
|
||||||
|
args := c.Called(resourceID)
|
||||||
|
|
||||||
|
return args.Get(0).(ResourceRoles), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRoleAssignments implements Client for testing.
|
||||||
|
func (c *MockClient) ListRoleAssignments(ctx context.Context, roleID gidx.PrefixedID) ([]gidx.PrefixedID, error) {
|
||||||
|
args := c.Called(roleID)
|
||||||
|
|
||||||
|
return args.Get(0).([]gidx.PrefixedID), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoleHasAssignment implements Client for testing.
|
||||||
|
func (c *MockClient) RoleHasAssignment(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) (bool, error) {
|
||||||
|
args := c.Called(roleID, memberID)
|
||||||
|
|
||||||
|
return args.Bool(0), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnassignRole implements Client for testing.
|
||||||
|
func (c *MockClient) UnassignRole(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) error {
|
||||||
|
args := c.Called(roleID, memberID)
|
||||||
|
|
||||||
|
return args.Error(0)
|
||||||
|
}
|
||||||
357
internal/service/organizations_test.go
Normal file
357
internal/service/organizations_test.go
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
package service_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.infratographer.com/x/events"
|
||||||
|
"go.infratographer.com/x/gidx"
|
||||||
|
|
||||||
|
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/models"
|
||||||
|
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/providers"
|
||||||
|
"go.equinixmetal.net/infra9-metal-bridge/internal/permissions"
|
||||||
|
"go.equinixmetal.net/infra9-metal-bridge/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockPublisher struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *mockPublisher) PublishChange(ctx context.Context, subjectType string, change events.ChangeMessage) error {
|
||||||
|
args := p.Called(subjectType, change)
|
||||||
|
|
||||||
|
return args.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTouchOrganizationEmpty(t *testing.T) {
|
||||||
|
rootTenantID := gidx.PrefixedID("tnntten-root1")
|
||||||
|
|
||||||
|
roleMap := map[string][]string{
|
||||||
|
"owner": {
|
||||||
|
"action1",
|
||||||
|
"action2",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
userID := gidx.PrefixedID("idntusr-2s-9kVNPJBaInlHpRs3lAMsvU_kVkLaSlD4R_RhavDw")
|
||||||
|
user := &models.UserDetails{
|
||||||
|
ID: "usr1",
|
||||||
|
}
|
||||||
|
|
||||||
|
projectID := gidx.PrefixedID("metlprj-prj1")
|
||||||
|
project := &models.ProjectDetails{
|
||||||
|
ID: "prj1",
|
||||||
|
}
|
||||||
|
|
||||||
|
orgID := gidx.PrefixedID("metlorg-org1")
|
||||||
|
org := &models.OrganizationDetails{
|
||||||
|
ID: "org1",
|
||||||
|
Projects: []*models.ProjectDetails{
|
||||||
|
project,
|
||||||
|
},
|
||||||
|
Memberships: []*models.Membership[models.OrganizationDetails]{
|
||||||
|
{
|
||||||
|
User: user,
|
||||||
|
Roles: []string{
|
||||||
|
"owner",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
mMetal = new(providers.MockProvider)
|
||||||
|
mPerms = new(permissions.MockClient)
|
||||||
|
mPublisher = new(mockPublisher)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
|
||||||
|
mMetal.On("GetOrganizationDetails", orgID).Return(org, nil)
|
||||||
|
|
||||||
|
mPerms.On("ListResourceRelationshipsFrom", orgID).Return([]permissions.ResourceRelationship{}, nil)
|
||||||
|
mPerms.On("ListResourceRelationshipsTo", orgID).Return([]permissions.ResourceRelationship{}, nil)
|
||||||
|
|
||||||
|
orgTenantChangeMessage := events.ChangeMessage{
|
||||||
|
SubjectID: orgID,
|
||||||
|
EventType: string(events.CreateChangeType),
|
||||||
|
AdditionalSubjectIDs: []gidx.PrefixedID{
|
||||||
|
rootTenantID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
relateProjectChangeMessage := events.ChangeMessage{
|
||||||
|
SubjectID: projectID,
|
||||||
|
EventType: string(events.CreateChangeType),
|
||||||
|
AdditionalSubjectIDs: []gidx.PrefixedID{
|
||||||
|
orgID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mPublisher.On("PublishChange", "metalorganization", orgTenantChangeMessage).Return(nil)
|
||||||
|
mPublisher.On("PublishChange", "metalorganization", relateProjectChangeMessage).Return(nil)
|
||||||
|
|
||||||
|
// Memberships
|
||||||
|
|
||||||
|
newRoleID := gidx.PrefixedID("permrol-role1")
|
||||||
|
|
||||||
|
mPerms.On("ListResourceRoles", orgID).Return(permissions.ResourceRoles{}, nil)
|
||||||
|
mPerms.On("CreateRole", orgID, roleMap["owner"]).Return(newRoleID, nil)
|
||||||
|
|
||||||
|
mPerms.On("AssignRole", newRoleID, userID).Return(nil)
|
||||||
|
|
||||||
|
// Run scenario
|
||||||
|
|
||||||
|
svc, err := service.New(mPublisher, mMetal, mPerms,
|
||||||
|
service.WithRootTenant(rootTenantID.String()),
|
||||||
|
service.WithRoles(roleMap),
|
||||||
|
)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = svc.TouchOrganization(context.Background(), orgID)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.True(t, mMetal.AssertExpectations(t), "unexpected calls to metal provider")
|
||||||
|
require.True(t, mPerms.AssertExpectations(t), "unexpected calls to permissions client")
|
||||||
|
require.True(t, mPublisher.AssertExpectations(t), "unexpected calls to events publisher")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTouchOrganizationCleanup(t *testing.T) {
|
||||||
|
oldRootTenantID := gidx.PrefixedID("tnntten-root1")
|
||||||
|
newRootTenantID := gidx.PrefixedID("tnntten-root2")
|
||||||
|
|
||||||
|
roleMap := map[string][]string{
|
||||||
|
"owner": {
|
||||||
|
"action1",
|
||||||
|
"action2",
|
||||||
|
},
|
||||||
|
"collaborator": {
|
||||||
|
"action1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
oldUserID := gidx.PrefixedID("idntusr-2s-9kVNPJBaInlHpRs3lAMsvU_kVkLaSlD4R_RhavDw")
|
||||||
|
deadProjectID := gidx.PrefixedID("metlprj-prj1")
|
||||||
|
|
||||||
|
userID := gidx.PrefixedID("idntusr-RnKvdujrwqm4o1dBDgfgaqeCpKFMaGeOtGnNbZky0Kg")
|
||||||
|
user := &models.UserDetails{
|
||||||
|
ID: "usr2",
|
||||||
|
}
|
||||||
|
|
||||||
|
projectID := gidx.PrefixedID("metlprj-prj2")
|
||||||
|
project := &models.ProjectDetails{
|
||||||
|
ID: "prj2",
|
||||||
|
}
|
||||||
|
|
||||||
|
orgID := gidx.PrefixedID("metlorg-org1")
|
||||||
|
org := &models.OrganizationDetails{
|
||||||
|
ID: "org1",
|
||||||
|
Projects: []*models.ProjectDetails{
|
||||||
|
project,
|
||||||
|
},
|
||||||
|
Memberships: []*models.Membership[models.OrganizationDetails]{
|
||||||
|
{
|
||||||
|
User: user,
|
||||||
|
Roles: []string{
|
||||||
|
"owner",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
mMetal = new(providers.MockProvider)
|
||||||
|
mPerms = new(permissions.MockClient)
|
||||||
|
mPublisher = new(mockPublisher)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
|
||||||
|
mMetal.On("GetOrganizationDetails", orgID).Return(org, nil)
|
||||||
|
|
||||||
|
existingRelsFrom := []permissions.ResourceRelationship{
|
||||||
|
{
|
||||||
|
Relation: string(service.RelateParent),
|
||||||
|
SubjectID: oldRootTenantID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mPerms.On("ListResourceRelationshipsFrom", orgID).Return(existingRelsFrom, nil)
|
||||||
|
|
||||||
|
existingRelsTo := []permissions.ResourceRelationship{
|
||||||
|
{
|
||||||
|
ResourceID: deadProjectID,
|
||||||
|
Relation: string(service.RelateParent),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mPerms.On("ListResourceRelationshipsTo", orgID).Return(existingRelsTo, nil)
|
||||||
|
|
||||||
|
mPerms.On("DeleteResourceRelationship", orgID, string(service.RelateParent), oldRootTenantID).Return(nil)
|
||||||
|
mPerms.On("DeleteResourceRelationship", deadProjectID, string(service.RelateParent), orgID).Return(nil)
|
||||||
|
|
||||||
|
orgTenantChangeMessage := events.ChangeMessage{
|
||||||
|
SubjectID: orgID,
|
||||||
|
EventType: string(events.CreateChangeType),
|
||||||
|
AdditionalSubjectIDs: []gidx.PrefixedID{
|
||||||
|
newRootTenantID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
relateProjectChangeMessage := events.ChangeMessage{
|
||||||
|
SubjectID: projectID,
|
||||||
|
EventType: string(events.CreateChangeType),
|
||||||
|
AdditionalSubjectIDs: []gidx.PrefixedID{
|
||||||
|
orgID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mPublisher.On("PublishChange", "metalorganization", orgTenantChangeMessage).Return(nil)
|
||||||
|
mPublisher.On("PublishChange", "metalorganization", relateProjectChangeMessage).Return(nil)
|
||||||
|
|
||||||
|
// Memberships
|
||||||
|
|
||||||
|
oldRoleID := gidx.PrefixedID("permrol-role1")
|
||||||
|
newRoleID := gidx.PrefixedID("permrol-role2")
|
||||||
|
|
||||||
|
existingRoles := permissions.ResourceRoles{
|
||||||
|
{
|
||||||
|
ID: oldRoleID,
|
||||||
|
Actions: roleMap["collaborator"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mPerms.On("ListResourceRoles", orgID).Return(existingRoles, nil)
|
||||||
|
|
||||||
|
existingRoleAssignments := []gidx.PrefixedID{
|
||||||
|
oldUserID,
|
||||||
|
}
|
||||||
|
|
||||||
|
mPerms.On("ListRoleAssignments", oldRoleID).Return(existingRoleAssignments, nil)
|
||||||
|
|
||||||
|
mPerms.On("CreateRole", orgID, roleMap["owner"]).Return(newRoleID, nil)
|
||||||
|
mPerms.On("DeleteRole", oldRoleID).Return(nil)
|
||||||
|
|
||||||
|
mPerms.On("AssignRole", newRoleID, userID).Return(nil)
|
||||||
|
mPerms.On("UnassignRole", oldRoleID, oldUserID).Return(nil)
|
||||||
|
|
||||||
|
// Run scenario
|
||||||
|
|
||||||
|
svc, err := service.New(mPublisher, mMetal, mPerms,
|
||||||
|
service.WithRootTenant(newRootTenantID.String()),
|
||||||
|
service.WithRoles(roleMap),
|
||||||
|
)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = svc.TouchOrganization(context.Background(), orgID)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.True(t, mMetal.AssertExpectations(t), "unexpected calls to metal provider")
|
||||||
|
require.True(t, mPerms.AssertExpectations(t), "unexpected calls to permissions client")
|
||||||
|
require.True(t, mPublisher.AssertExpectations(t), "unexpected calls to events publisher")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTouchOrganizationNoChange(t *testing.T) {
|
||||||
|
rootTenantID := gidx.PrefixedID("tnntten-root1")
|
||||||
|
|
||||||
|
roleMap := map[string][]string{
|
||||||
|
"owner": {
|
||||||
|
"action1",
|
||||||
|
"action2",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
userID := gidx.PrefixedID("idntusr-2s-9kVNPJBaInlHpRs3lAMsvU_kVkLaSlD4R_RhavDw")
|
||||||
|
user := &models.UserDetails{
|
||||||
|
ID: "usr1",
|
||||||
|
}
|
||||||
|
|
||||||
|
projectID := gidx.PrefixedID("metlprj-prj1")
|
||||||
|
project := &models.ProjectDetails{
|
||||||
|
ID: "prj1",
|
||||||
|
}
|
||||||
|
|
||||||
|
orgID := gidx.PrefixedID("metlorg-org1")
|
||||||
|
org := &models.OrganizationDetails{
|
||||||
|
ID: "org1",
|
||||||
|
Projects: []*models.ProjectDetails{
|
||||||
|
project,
|
||||||
|
},
|
||||||
|
Memberships: []*models.Membership[models.OrganizationDetails]{
|
||||||
|
{
|
||||||
|
User: user,
|
||||||
|
Roles: []string{
|
||||||
|
"owner",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
mMetal = new(providers.MockProvider)
|
||||||
|
mPerms = new(permissions.MockClient)
|
||||||
|
mPublisher = new(mockPublisher)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
|
||||||
|
mMetal.On("GetOrganizationDetails", orgID).Return(org, nil)
|
||||||
|
|
||||||
|
existingRelsFrom := []permissions.ResourceRelationship{
|
||||||
|
{
|
||||||
|
Relation: string(service.RelateParent),
|
||||||
|
SubjectID: rootTenantID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mPerms.On("ListResourceRelationshipsFrom", orgID).Return(existingRelsFrom, nil)
|
||||||
|
|
||||||
|
existingRelsTo := []permissions.ResourceRelationship{
|
||||||
|
{
|
||||||
|
ResourceID: projectID,
|
||||||
|
Relation: string(service.RelateParent),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mPerms.On("ListResourceRelationshipsTo", orgID).Return(existingRelsTo, nil)
|
||||||
|
|
||||||
|
// Memberships
|
||||||
|
|
||||||
|
roleID := gidx.PrefixedID("permrol-role1")
|
||||||
|
|
||||||
|
existingRoles := permissions.ResourceRoles{
|
||||||
|
{
|
||||||
|
ID: roleID,
|
||||||
|
Actions: roleMap["owner"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mPerms.On("ListResourceRoles", orgID).Return(existingRoles, nil)
|
||||||
|
|
||||||
|
existingRoleAssignments := []gidx.PrefixedID{
|
||||||
|
userID,
|
||||||
|
}
|
||||||
|
|
||||||
|
mPerms.On("ListRoleAssignments", roleID).Return(existingRoleAssignments, nil)
|
||||||
|
|
||||||
|
// Run scenario
|
||||||
|
|
||||||
|
svc, err := service.New(mPublisher, mMetal, mPerms,
|
||||||
|
service.WithRootTenant(rootTenantID.String()),
|
||||||
|
service.WithRoles(roleMap),
|
||||||
|
)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = svc.TouchOrganization(context.Background(), orgID)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.True(t, mMetal.AssertExpectations(t), "unexpected calls to metal provider")
|
||||||
|
require.True(t, mPerms.AssertExpectations(t), "unexpected calls to permissions client")
|
||||||
|
require.True(t, mPublisher.AssertExpectations(t), "unexpected calls to events publisher")
|
||||||
|
}
|
||||||
303
internal/service/projects_test.go
Normal file
303
internal/service/projects_test.go
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
package service_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.infratographer.com/x/events"
|
||||||
|
"go.infratographer.com/x/gidx"
|
||||||
|
|
||||||
|
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/models"
|
||||||
|
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/providers"
|
||||||
|
"go.equinixmetal.net/infra9-metal-bridge/internal/permissions"
|
||||||
|
"go.equinixmetal.net/infra9-metal-bridge/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTouchProjectEmpty(t *testing.T) {
|
||||||
|
rootTenantID := gidx.PrefixedID("tnntten-root1")
|
||||||
|
|
||||||
|
roleMap := map[string][]string{
|
||||||
|
"owner": {
|
||||||
|
"action1",
|
||||||
|
"action2",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
userID := gidx.PrefixedID("idntusr-2s-9kVNPJBaInlHpRs3lAMsvU_kVkLaSlD4R_RhavDw")
|
||||||
|
user := &models.UserDetails{
|
||||||
|
ID: "usr1",
|
||||||
|
}
|
||||||
|
|
||||||
|
orgID := gidx.PrefixedID("metlorg-org1")
|
||||||
|
org := &models.OrganizationDetails{
|
||||||
|
ID: "org1",
|
||||||
|
}
|
||||||
|
|
||||||
|
projectID := gidx.PrefixedID("metlprj-prj1")
|
||||||
|
project := &models.ProjectDetails{
|
||||||
|
ID: "prj1",
|
||||||
|
Organization: org,
|
||||||
|
Memberships: []*models.Membership[models.ProjectDetails]{
|
||||||
|
{
|
||||||
|
User: user,
|
||||||
|
Roles: []string{
|
||||||
|
"owner",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
mMetal = new(providers.MockProvider)
|
||||||
|
mPerms = new(permissions.MockClient)
|
||||||
|
mPublisher = new(mockPublisher)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
|
||||||
|
mMetal.On("GetProjectDetails", projectID).Return(project, nil)
|
||||||
|
|
||||||
|
mPerms.On("ListResourceRelationshipsFrom", projectID).Return([]permissions.ResourceRelationship{}, nil)
|
||||||
|
|
||||||
|
relateParentChangeMessage := events.ChangeMessage{
|
||||||
|
SubjectID: projectID,
|
||||||
|
EventType: string(events.CreateChangeType),
|
||||||
|
AdditionalSubjectIDs: []gidx.PrefixedID{
|
||||||
|
orgID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mPublisher.On("PublishChange", "metalproject", relateParentChangeMessage).Return(nil)
|
||||||
|
|
||||||
|
// Memberships
|
||||||
|
|
||||||
|
newRoleID := gidx.PrefixedID("permrol-role1")
|
||||||
|
|
||||||
|
mPerms.On("ListResourceRoles", projectID).Return(permissions.ResourceRoles{}, nil)
|
||||||
|
mPerms.On("CreateRole", projectID, roleMap["owner"]).Return(newRoleID, nil)
|
||||||
|
|
||||||
|
mPerms.On("AssignRole", newRoleID, userID).Return(nil)
|
||||||
|
|
||||||
|
// Run scenario
|
||||||
|
|
||||||
|
svc, err := service.New(mPublisher, mMetal, mPerms,
|
||||||
|
service.WithRootTenant(rootTenantID.String()),
|
||||||
|
service.WithRoles(roleMap),
|
||||||
|
)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = svc.TouchProject(context.Background(), projectID)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.True(t, mMetal.AssertExpectations(t), "unexpected calls to metal provider")
|
||||||
|
require.True(t, mPerms.AssertExpectations(t), "unexpected calls to permissions client")
|
||||||
|
require.True(t, mPublisher.AssertExpectations(t), "unexpected calls to events publisher")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTouchProjectCleanup(t *testing.T) {
|
||||||
|
rootTenantID := gidx.PrefixedID("tnntten-root1")
|
||||||
|
|
||||||
|
roleMap := map[string][]string{
|
||||||
|
"owner": {
|
||||||
|
"action1",
|
||||||
|
"action2",
|
||||||
|
},
|
||||||
|
"collaborator": {
|
||||||
|
"action1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
oldUserID := gidx.PrefixedID("idntusr-2s-9kVNPJBaInlHpRs3lAMsvU_kVkLaSlD4R_RhavDw")
|
||||||
|
oldOrgID := gidx.PrefixedID("metlorg-org1")
|
||||||
|
|
||||||
|
userID := gidx.PrefixedID("idntusr-RnKvdujrwqm4o1dBDgfgaqeCpKFMaGeOtGnNbZky0Kg")
|
||||||
|
user := &models.UserDetails{
|
||||||
|
ID: "usr2",
|
||||||
|
}
|
||||||
|
|
||||||
|
orgID := gidx.PrefixedID("metlorg-org2")
|
||||||
|
org := &models.OrganizationDetails{
|
||||||
|
ID: "org2",
|
||||||
|
}
|
||||||
|
|
||||||
|
projectID := gidx.PrefixedID("metlprj-prj2")
|
||||||
|
project := &models.ProjectDetails{
|
||||||
|
ID: "prj2",
|
||||||
|
Organization: org,
|
||||||
|
Memberships: []*models.Membership[models.ProjectDetails]{
|
||||||
|
{
|
||||||
|
User: user,
|
||||||
|
Roles: []string{
|
||||||
|
"owner",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
mMetal = new(providers.MockProvider)
|
||||||
|
mPerms = new(permissions.MockClient)
|
||||||
|
mPublisher = new(mockPublisher)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
|
||||||
|
mMetal.On("GetProjectDetails", projectID).Return(project, nil)
|
||||||
|
|
||||||
|
existingRelsFrom := []permissions.ResourceRelationship{
|
||||||
|
{
|
||||||
|
Relation: string(service.RelateParent),
|
||||||
|
SubjectID: oldOrgID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mPerms.On("ListResourceRelationshipsFrom", projectID).Return(existingRelsFrom, nil)
|
||||||
|
|
||||||
|
mPerms.On("DeleteResourceRelationship", projectID, string(service.RelateParent), oldOrgID).Return(nil)
|
||||||
|
|
||||||
|
relateParentChangeMessage := events.ChangeMessage{
|
||||||
|
SubjectID: projectID,
|
||||||
|
EventType: string(events.CreateChangeType),
|
||||||
|
AdditionalSubjectIDs: []gidx.PrefixedID{
|
||||||
|
orgID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mPublisher.On("PublishChange", "metalproject", relateParentChangeMessage).Return(nil)
|
||||||
|
|
||||||
|
// Memberships
|
||||||
|
|
||||||
|
oldRoleID := gidx.PrefixedID("permrol-role1")
|
||||||
|
newRoleID := gidx.PrefixedID("permrol-role2")
|
||||||
|
|
||||||
|
existingRoles := permissions.ResourceRoles{
|
||||||
|
{
|
||||||
|
ID: oldRoleID,
|
||||||
|
Actions: roleMap["collaborator"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mPerms.On("ListResourceRoles", projectID).Return(existingRoles, nil)
|
||||||
|
|
||||||
|
existingRoleAssignments := []gidx.PrefixedID{
|
||||||
|
oldUserID,
|
||||||
|
}
|
||||||
|
|
||||||
|
mPerms.On("ListRoleAssignments", oldRoleID).Return(existingRoleAssignments, nil)
|
||||||
|
|
||||||
|
mPerms.On("CreateRole", projectID, roleMap["owner"]).Return(newRoleID, nil)
|
||||||
|
mPerms.On("DeleteRole", oldRoleID).Return(nil)
|
||||||
|
|
||||||
|
mPerms.On("AssignRole", newRoleID, userID).Return(nil)
|
||||||
|
mPerms.On("UnassignRole", oldRoleID, oldUserID).Return(nil)
|
||||||
|
|
||||||
|
// Run scenario
|
||||||
|
|
||||||
|
svc, err := service.New(mPublisher, mMetal, mPerms,
|
||||||
|
service.WithRootTenant(rootTenantID.String()),
|
||||||
|
service.WithRoles(roleMap),
|
||||||
|
)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = svc.TouchProject(context.Background(), projectID)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.True(t, mMetal.AssertExpectations(t), "unexpected calls to metal provider")
|
||||||
|
require.True(t, mPerms.AssertExpectations(t), "unexpected calls to permissions client")
|
||||||
|
require.True(t, mPublisher.AssertExpectations(t), "unexpected calls to events publisher")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTouchProjectNoChange(t *testing.T) {
|
||||||
|
rootTenantID := gidx.PrefixedID("tnntten-root1")
|
||||||
|
|
||||||
|
roleMap := map[string][]string{
|
||||||
|
"owner": {
|
||||||
|
"action1",
|
||||||
|
"action2",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
userID := gidx.PrefixedID("idntusr-2s-9kVNPJBaInlHpRs3lAMsvU_kVkLaSlD4R_RhavDw")
|
||||||
|
user := &models.UserDetails{
|
||||||
|
ID: "usr1",
|
||||||
|
}
|
||||||
|
|
||||||
|
orgID := gidx.PrefixedID("metlorg-org1")
|
||||||
|
org := &models.OrganizationDetails{
|
||||||
|
ID: "org1",
|
||||||
|
}
|
||||||
|
|
||||||
|
projectID := gidx.PrefixedID("metlprj-prj1")
|
||||||
|
project := &models.ProjectDetails{
|
||||||
|
ID: "prj1",
|
||||||
|
Organization: org,
|
||||||
|
Memberships: []*models.Membership[models.ProjectDetails]{
|
||||||
|
{
|
||||||
|
User: user,
|
||||||
|
Roles: []string{
|
||||||
|
"owner",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
mMetal = new(providers.MockProvider)
|
||||||
|
mPerms = new(permissions.MockClient)
|
||||||
|
mPublisher = new(mockPublisher)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
|
||||||
|
mMetal.On("GetProjectDetails", projectID).Return(project, nil)
|
||||||
|
|
||||||
|
existingRelsFrom := []permissions.ResourceRelationship{
|
||||||
|
{
|
||||||
|
Relation: string(service.RelateParent),
|
||||||
|
SubjectID: orgID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mPerms.On("ListResourceRelationshipsFrom", projectID).Return(existingRelsFrom, nil)
|
||||||
|
|
||||||
|
// Memberships
|
||||||
|
|
||||||
|
roleID := gidx.PrefixedID("permrol-role1")
|
||||||
|
|
||||||
|
existingRoles := permissions.ResourceRoles{
|
||||||
|
{
|
||||||
|
ID: roleID,
|
||||||
|
Actions: roleMap["owner"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mPerms.On("ListResourceRoles", projectID).Return(existingRoles, nil)
|
||||||
|
|
||||||
|
existingRoleAssignments := []gidx.PrefixedID{
|
||||||
|
userID,
|
||||||
|
}
|
||||||
|
|
||||||
|
mPerms.On("ListRoleAssignments", roleID).Return(existingRoleAssignments, nil)
|
||||||
|
|
||||||
|
// Run scenario
|
||||||
|
|
||||||
|
svc, err := service.New(mPublisher, mMetal, mPerms,
|
||||||
|
service.WithRootTenant(rootTenantID.String()),
|
||||||
|
service.WithRoles(roleMap),
|
||||||
|
)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = svc.TouchProject(context.Background(), projectID)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.True(t, mMetal.AssertExpectations(t), "unexpected calls to metal provider")
|
||||||
|
require.True(t, mPerms.AssertExpectations(t), "unexpected calls to permissions client")
|
||||||
|
require.True(t, mPublisher.AssertExpectations(t), "unexpected calls to events publisher")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user