mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
# Which Problems Are Solved This PR implements the endpoints listed in https://github.com/zitadel/zitadel/issues/10444 . # How the Problems Are Solved The implementation follows the same pattern as the Instance one. The `Add` and `Remove` are implementing the `Commander` interface, while `List` Domains endpoints are implementing the `Querier` interace. The PR adds also some forgotten calls to the relational logic for the Instance API. The commits are identifiable by the label `instance:` prepended to them. This is the complete list: bafce01b0 , 6fe7257ab , e27bc9c34 , 8133ede73 , fde667953 , a3e1135ed , ee1a6f9c1 # Additional Context - Closes #10444 --------- Co-authored-by: Elio Bischof <[email protected]> Co-authored-by: Lebogang Phoshoko <[email protected]> Co-authored-by: Wim Van Laer <[email protected]> Co-authored-by: Max Peintner <[email protected]> Co-authored-by: Gayathri Vijayan <[email protected]>
338 lines
10 KiB
Go
338 lines
10 KiB
Go
package domain_test
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/mock/gomock"
|
|
|
|
"github.com/zitadel/zitadel/backend/v3/domain"
|
|
domainmock "github.com/zitadel/zitadel/backend/v3/domain/mock"
|
|
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
|
"github.com/zitadel/zitadel/backend/v3/storage/database/dbmock"
|
|
noopdb "github.com/zitadel/zitadel/backend/v3/storage/database/dialect/noop"
|
|
"github.com/zitadel/zitadel/internal/api/authz"
|
|
"github.com/zitadel/zitadel/internal/eventstore"
|
|
"github.com/zitadel/zitadel/internal/repository/instance"
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
|
)
|
|
|
|
func TestUpdateInstanceCommand_Validate(t *testing.T) {
|
|
t.Parallel()
|
|
getErr := errors.New("get error")
|
|
permissionErr := errors.New("permission error")
|
|
|
|
tt := []struct {
|
|
testName string
|
|
instanceRepo func(ctrl *gomock.Controller) domain.InstanceRepository
|
|
permissionChecker func(ctrl *gomock.Controller) domain.PermissionChecker
|
|
inputInstanceID string
|
|
inputInstanceName string
|
|
expectedError error
|
|
expectedUpdateSkip bool
|
|
}{
|
|
{
|
|
testName: "when no ID should return invalid argument error",
|
|
inputInstanceID: "",
|
|
inputInstanceName: "test-name",
|
|
expectedError: zerrors.ThrowInvalidArgument(nil, "DOM-wSs6kG", "Errors.Instance.ID"),
|
|
},
|
|
{
|
|
testName: "when no name shuld return invalid argument error",
|
|
inputInstanceID: "test-id",
|
|
inputInstanceName: "",
|
|
expectedError: zerrors.ThrowInvalidArgument(nil, "DOM-FPJcLC", "Errors.Instance.Name"),
|
|
},
|
|
{
|
|
testName: "when user is missing permission should return permission denied",
|
|
permissionChecker: func(ctrl *gomock.Controller) domain.PermissionChecker {
|
|
permChecker := domainmock.NewMockPermissionChecker(ctrl)
|
|
|
|
permChecker.EXPECT().
|
|
CheckInstancePermission(gomock.Any(), domain.InstanceWritePermission).
|
|
Times(1).
|
|
Return(permissionErr)
|
|
|
|
return permChecker
|
|
},
|
|
inputInstanceID: "instance-1",
|
|
inputInstanceName: "test instance update",
|
|
expectedError: zerrors.ThrowPermissionDenied(permissionErr, "DOM-M5ObLP", "Errors.PermissionDenied"),
|
|
},
|
|
{
|
|
testName: "when retrieving instance fails should return error",
|
|
permissionChecker: func(ctrl *gomock.Controller) domain.PermissionChecker {
|
|
permChecker := domainmock.NewMockPermissionChecker(ctrl)
|
|
|
|
permChecker.EXPECT().
|
|
CheckInstancePermission(gomock.Any(), domain.InstanceWritePermission).
|
|
Times(1).
|
|
Return(nil)
|
|
|
|
return permChecker
|
|
},
|
|
instanceRepo: func(ctrl *gomock.Controller) domain.InstanceRepository {
|
|
repo := domainmock.NewInstanceRepo(ctrl)
|
|
repo.EXPECT().
|
|
Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(
|
|
database.WithCondition(
|
|
repo.IDCondition("instance-1"),
|
|
),
|
|
)).
|
|
Times(1).
|
|
Return(nil, getErr)
|
|
return repo
|
|
},
|
|
inputInstanceID: "instance-1",
|
|
inputInstanceName: "test instance update",
|
|
expectedError: zerrors.ThrowInternal(getErr, "DOM-j05Hdo", "Errors.Instance.Get"),
|
|
},
|
|
{
|
|
testName: "when instance name is not changed should return name not changed error",
|
|
permissionChecker: func(ctrl *gomock.Controller) domain.PermissionChecker {
|
|
permChecker := domainmock.NewMockPermissionChecker(ctrl)
|
|
|
|
permChecker.EXPECT().
|
|
CheckInstancePermission(gomock.Any(), domain.InstanceWritePermission).
|
|
Times(1).
|
|
Return(nil)
|
|
|
|
return permChecker
|
|
},
|
|
instanceRepo: func(ctrl *gomock.Controller) domain.InstanceRepository {
|
|
repo := domainmock.NewInstanceRepo(ctrl)
|
|
repo.EXPECT().
|
|
Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(
|
|
repo.IDCondition("instance-1"),
|
|
))).
|
|
Times(1).
|
|
Return(&domain.Instance{
|
|
ID: "instance-1",
|
|
Name: "test instance update",
|
|
}, nil)
|
|
return repo
|
|
},
|
|
inputInstanceID: "instance-1",
|
|
inputInstanceName: "test instance update",
|
|
expectedUpdateSkip: true,
|
|
},
|
|
{
|
|
testName: "when instance name is changed should validate successfully and return no error",
|
|
permissionChecker: func(ctrl *gomock.Controller) domain.PermissionChecker {
|
|
permChecker := domainmock.NewMockPermissionChecker(ctrl)
|
|
|
|
permChecker.EXPECT().
|
|
CheckInstancePermission(gomock.Any(), domain.InstanceWritePermission).
|
|
Times(1).
|
|
Return(nil)
|
|
|
|
return permChecker
|
|
},
|
|
instanceRepo: func(ctrl *gomock.Controller) domain.InstanceRepository {
|
|
repo := domainmock.NewInstanceRepo(ctrl)
|
|
repo.EXPECT().
|
|
Get(gomock.Any(), gomock.Any(), dbmock.QueryOptions(database.WithCondition(
|
|
repo.IDCondition("instance-1"),
|
|
))).
|
|
Times(1).
|
|
Return(&domain.Instance{
|
|
ID: "instance-1",
|
|
Name: "old instance name",
|
|
}, nil)
|
|
return repo
|
|
},
|
|
inputInstanceID: "instance-1",
|
|
inputInstanceName: "test instance update",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tt {
|
|
t.Run(tc.testName, func(t *testing.T) {
|
|
t.Parallel()
|
|
// Given
|
|
ctx := authz.NewMockContext("instance-1", "", "")
|
|
ctrl := gomock.NewController(t)
|
|
cmd := domain.NewUpdateInstanceCommand(tc.inputInstanceID, tc.inputInstanceName)
|
|
|
|
opts := &domain.InvokeOpts{
|
|
Invoker: domain.NewTransactionInvoker(nil),
|
|
}
|
|
domain.WithQueryExecutor(new(noopdb.Pool))(opts)
|
|
if tc.instanceRepo != nil {
|
|
domain.WithInstanceRepo(tc.instanceRepo(ctrl))(opts)
|
|
}
|
|
if tc.permissionChecker != nil {
|
|
opts.Permissions = tc.permissionChecker(ctrl)
|
|
}
|
|
|
|
err := cmd.Validate(ctx, opts)
|
|
assert.Equal(t, tc.expectedError, err)
|
|
assert.Equal(t, tc.expectedUpdateSkip, cmd.ShouldSkipUpdate)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUpdateInstanceCommand_Execute(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
updateErr := errors.New("update error")
|
|
|
|
tt := []struct {
|
|
testName string
|
|
|
|
instanceRepo func(ctrl *gomock.Controller) domain.InstanceRepository
|
|
|
|
inputID string
|
|
inputName string
|
|
shouldSkipUpdate bool
|
|
|
|
expectedError error
|
|
}{
|
|
{
|
|
testName: "when ShouldSkipUpdate is true should return nil",
|
|
shouldSkipUpdate: true,
|
|
},
|
|
{
|
|
testName: "when instance update fails should return error",
|
|
instanceRepo: func(ctrl *gomock.Controller) domain.InstanceRepository {
|
|
repo := domainmock.NewInstanceRepo(ctrl)
|
|
repo.EXPECT().
|
|
Update(gomock.Any(), gomock.Any(),
|
|
"instance-1",
|
|
repo.SetName("test instance update"),
|
|
).
|
|
Times(1).
|
|
Return(int64(0), updateErr)
|
|
return repo
|
|
},
|
|
inputID: "instance-1",
|
|
inputName: "test instance update",
|
|
expectedError: zerrors.ThrowInternal(updateErr, "DOM-PkVMNR", "Errors.Instance.Update"),
|
|
},
|
|
{
|
|
testName: "when instance update returns 0 rows updated should return not found error",
|
|
inputID: "instance-1",
|
|
inputName: "test instance update",
|
|
instanceRepo: func(ctrl *gomock.Controller) domain.InstanceRepository {
|
|
repo := domainmock.NewInstanceRepo(ctrl)
|
|
repo.EXPECT().
|
|
Update(gomock.Any(), gomock.Any(),
|
|
"instance-1",
|
|
repo.SetName("test instance update"),
|
|
).
|
|
Times(1).
|
|
Return(int64(0), nil)
|
|
return repo
|
|
},
|
|
expectedError: zerrors.ThrowNotFound(nil, "DOM-ghfov1", "Errors.Instance.NotFound"),
|
|
},
|
|
{
|
|
testName: "when instance update returns more than 1 row updated should return internal error",
|
|
instanceRepo: func(ctrl *gomock.Controller) domain.InstanceRepository {
|
|
repo := domainmock.NewInstanceRepo(ctrl)
|
|
repo.EXPECT().
|
|
Update(gomock.Any(), gomock.Any(),
|
|
"instance-1",
|
|
repo.SetName("test instance update"),
|
|
).
|
|
Times(1).
|
|
Return(int64(2), nil)
|
|
return repo
|
|
},
|
|
inputID: "instance-1",
|
|
inputName: "test instance update",
|
|
expectedError: zerrors.ThrowInternal(domain.NewMultipleObjectsUpdatedError(1, 2), "DOM-HlrNmD", "Errors.Instance.UpdateMismatch"),
|
|
},
|
|
{
|
|
testName: "when instance update returns 1 row updated should return no error and set non-primary verified domain",
|
|
instanceRepo: func(ctrl *gomock.Controller) domain.InstanceRepository {
|
|
repo := domainmock.NewInstanceRepo(ctrl)
|
|
|
|
repo.EXPECT().
|
|
Update(gomock.Any(), gomock.Any(),
|
|
"instance-1",
|
|
repo.SetName("test instance update"),
|
|
).
|
|
Times(1).
|
|
Return(int64(1), nil)
|
|
return repo
|
|
},
|
|
inputID: "instance-1",
|
|
inputName: "test instance update",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tt {
|
|
t.Run(tc.testName, func(t *testing.T) {
|
|
t.Parallel()
|
|
// Given
|
|
ctx := authz.NewMockContext("instance-1", "", "")
|
|
ctrl := gomock.NewController(t)
|
|
cmd := domain.NewUpdateInstanceCommand(tc.inputID, tc.inputName)
|
|
cmd.ShouldSkipUpdate = tc.shouldSkipUpdate
|
|
|
|
opts := &domain.InvokeOpts{
|
|
Invoker: domain.NewTransactionInvoker(nil),
|
|
}
|
|
domain.WithQueryExecutor(new(noopdb.Pool))(opts)
|
|
if tc.instanceRepo != nil {
|
|
domain.WithInstanceRepo(tc.instanceRepo(ctrl))(opts)
|
|
}
|
|
|
|
// Test
|
|
err := cmd.Execute(ctx, opts)
|
|
|
|
// Verify
|
|
assert.Equal(t, tc.expectedError, err)
|
|
})
|
|
}
|
|
}
|
|
func TestUpdateInstanceCommand_Events(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tt := []struct {
|
|
testName string
|
|
inputName string
|
|
inputInstanceID string
|
|
shouldSkipUpdate bool
|
|
expectedEvent eventstore.Command
|
|
}{
|
|
{
|
|
testName: "when ShouldSkipUpdate is true should return no event and no error",
|
|
},
|
|
{
|
|
testName: "when ShouldSkipUpdate is true should return expected event",
|
|
inputName: "test-name",
|
|
inputInstanceID: "instance-1",
|
|
expectedEvent: instance.NewInstanceChangedEvent(
|
|
t.Context(),
|
|
&instance.NewAggregate("instance-1").Aggregate,
|
|
"test-name",
|
|
),
|
|
},
|
|
}
|
|
|
|
for _, tc := range tt {
|
|
t.Run(tc.testName, func(t *testing.T) {
|
|
// Given
|
|
ctx := authz.NewMockContext(tc.inputInstanceID, "", "")
|
|
cmd := domain.NewUpdateInstanceCommand(tc.inputInstanceID, tc.inputName)
|
|
|
|
// Test
|
|
events, err := cmd.Events(ctx, &domain.InvokeOpts{})
|
|
|
|
// Verify
|
|
assert.NoError(t, err)
|
|
if tc.expectedEvent != nil {
|
|
require.Len(t, events, 1)
|
|
|
|
event := events[0].(*instance.InstanceChangedEvent)
|
|
assert.Equal(t, tc.inputInstanceID, event.Aggregate().ID)
|
|
assert.Equal(t, tc.inputName, event.Name)
|
|
}
|
|
})
|
|
}
|
|
}
|