mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-29 18:42:15 +00:00
# Which Problems Are Solved Currently, to delete metadata a key, callers must use a separate [DeleteMetadata](https://zitadel.com/docs/reference/api/user/zitadel.user.v2.UserService.DeleteUserMetadata) API. This increases the complexity on client-side for operations where metadata need to be synchronized. # How the Problems Are Solved Introduce the behavior to [SetUserMetadata](https://zitadel.com/docs/reference/api/user/zitadel.user.v2.UserService.SetUserMetadata): When a metadata entry is passed with an empty value ("" / empty bytes): - If the key exists → delete the key - If the key does not exist → no-op (idempotent, no error) # Additional Changes - Updated [User Metadata page](https://zitadel.com/docs/guides/manage/customize/user-metadata#manage-user-metadata-through-the-management-api) # Additional Context Similar behavior will be applied to [UpdateUser](https://zitadel.com/docs/reference/api/user/zitadel.user.v2.UserService.UpdateUser) and [SetOrganizationMetadata](https://zitadel.com/docs/reference/api/org/zitadel.org.v2.OrganizationService.SetOrganizationMetadata) Breaking change? Currently, sending an empty value returns an error — no client should be intentionally relying on that error as part of a working workflow. The change goes from "rejected input" to "accepted input with defined semantics." In other words, it relaxes a restriction rather than tightening one. Existing valid calls continue to work exactly as before. The only scenario where it could be "breaking" is if someone explicitly depends on the error response for empty values (e.g., using it as a validation check), which would be unusual. --------- Co-authored-by: Gayathri Vijayan <[email protected]> Co-authored-by: Copilot <[email protected]>
624 lines
23 KiB
Go
624 lines
23 KiB
Go
//go:build integration
|
|
|
|
package user_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/zitadel/zitadel/internal/integration"
|
|
"github.com/zitadel/zitadel/pkg/grpc/filter/v2"
|
|
metadata "github.com/zitadel/zitadel/pkg/grpc/metadata/v2"
|
|
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
|
)
|
|
|
|
func TestServer_SetUserMetadata(t *testing.T) {
|
|
iamOwnerCTX := Instance.WithAuthorizationToken(OrgCTX, integration.UserTypeIAMOwner)
|
|
|
|
tests := []struct {
|
|
name string
|
|
ctx context.Context
|
|
dep func(request *user.SetUserMetadataRequest)
|
|
req *user.SetUserMetadataRequest
|
|
assert func(t *testing.T, setResponse *user.SetUserMetadataResponse, testStartTime time.Time, getResponse *user.ListUserMetadataResponse)
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "missing permission",
|
|
ctx: Instance.WithAuthorizationToken(context.Background(), integration.UserTypeNoPermission),
|
|
dep: func(req *user.SetUserMetadataRequest) {
|
|
req.UserId = Instance.CreateUserTypeHuman(OrgCTX, integration.Email()).GetId()
|
|
},
|
|
req: &user.SetUserMetadataRequest{
|
|
Metadata: []*user.Metadata{{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))}},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "set user metadata",
|
|
ctx: iamOwnerCTX,
|
|
dep: func(req *user.SetUserMetadataRequest) {
|
|
req.UserId = Instance.CreateUserTypeHuman(OrgCTX, integration.Email()).GetId()
|
|
},
|
|
req: &user.SetUserMetadataRequest{
|
|
Metadata: []*user.Metadata{{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))}},
|
|
},
|
|
assert: func(t *testing.T, setResponse *user.SetUserMetadataResponse, testStartTime time.Time, getResponse *user.ListUserMetadataResponse) {
|
|
assert.WithinRange(t, setResponse.GetSetDate().AsTime(), testStartTime, time.Now().UTC())
|
|
|
|
expectedMetadata := []*metadata.Metadata{
|
|
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
|
|
}
|
|
assertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
|
|
},
|
|
},
|
|
{
|
|
name: "set user metadata, multiple",
|
|
ctx: iamOwnerCTX,
|
|
dep: func(req *user.SetUserMetadataRequest) {
|
|
req.UserId = Instance.CreateUserTypeHuman(OrgCTX, integration.Email()).GetId()
|
|
},
|
|
req: &user.SetUserMetadataRequest{
|
|
Metadata: []*user.Metadata{
|
|
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
|
|
{Key: "key2", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value2")))},
|
|
{Key: "key3", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value3")))},
|
|
},
|
|
},
|
|
assert: func(t *testing.T, setResponse *user.SetUserMetadataResponse, testStartTime time.Time, getResponse *user.ListUserMetadataResponse) {
|
|
assert.WithinRange(t, setResponse.GetSetDate().AsTime(), testStartTime, time.Now().UTC())
|
|
|
|
expectedMetadata := []*metadata.Metadata{
|
|
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
|
|
{Key: "key2", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value2")))},
|
|
{Key: "key3", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value3")))},
|
|
}
|
|
assertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
|
|
},
|
|
},
|
|
{
|
|
name: "set user metadata on non existent user",
|
|
ctx: iamOwnerCTX,
|
|
req: &user.SetUserMetadataRequest{
|
|
UserId: "notexisting",
|
|
Metadata: []*user.Metadata{{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))}},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "update user metadata",
|
|
ctx: Instance.WithAuthorizationToken(OrgCTX, integration.UserTypeIAMOwner),
|
|
dep: func(req *user.SetUserMetadataRequest) {
|
|
req.UserId = Instance.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
Instance.SetUserMetadata(iamOwnerCTX, req.UserId, "key1", "value1")
|
|
},
|
|
req: &user.SetUserMetadataRequest{
|
|
Metadata: []*user.Metadata{{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value2")))}},
|
|
},
|
|
assert: func(t *testing.T, setResponse *user.SetUserMetadataResponse, testStartTime time.Time, getResponse *user.ListUserMetadataResponse) {
|
|
assert.WithinRange(t, setResponse.GetSetDate().AsTime(), testStartTime, time.Now().UTC())
|
|
|
|
expectedMetadata := []*metadata.Metadata{
|
|
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value2")))},
|
|
}
|
|
assertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
|
|
},
|
|
},
|
|
{
|
|
name: "update user metadata with same value",
|
|
ctx: Instance.WithAuthorizationToken(OrgCTX, integration.UserTypeIAMOwner),
|
|
dep: func(req *user.SetUserMetadataRequest) {
|
|
req.UserId = Instance.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
Instance.SetUserMetadata(iamOwnerCTX, req.UserId, "key1", "value1")
|
|
},
|
|
req: &user.SetUserMetadataRequest{
|
|
Metadata: []*user.Metadata{{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))}},
|
|
},
|
|
assert: func(t *testing.T, setResponse *user.SetUserMetadataResponse, testStartTime time.Time, getResponse *user.ListUserMetadataResponse) {
|
|
// assert that the set date is not updated, i.e., it is before testStartTime, because no change to the metadata should be made
|
|
assert.True(t, setResponse.GetSetDate().AsTime().Before(testStartTime))
|
|
|
|
expectedMetadata := []*metadata.Metadata{
|
|
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
|
|
}
|
|
assertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
|
|
},
|
|
},
|
|
{
|
|
name: "delete user metadata",
|
|
ctx: Instance.WithAuthorizationToken(OrgCTX, integration.UserTypeIAMOwner),
|
|
dep: func(req *user.SetUserMetadataRequest) {
|
|
req.UserId = Instance.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
Instance.SetUserMetadata(iamOwnerCTX, req.UserId, "key1", "value1")
|
|
},
|
|
req: &user.SetUserMetadataRequest{
|
|
Metadata: []*user.Metadata{{Key: "key1", Value: nil}},
|
|
},
|
|
assert: func(t *testing.T, setResponse *user.SetUserMetadataResponse, testStartTime time.Time, getResponse *user.ListUserMetadataResponse) {
|
|
assert.WithinRange(t, setResponse.GetSetDate().AsTime(), testStartTime, time.Now().UTC())
|
|
|
|
assert.Len(t, getResponse.GetMetadata(), 0)
|
|
},
|
|
},
|
|
{
|
|
name: "delete non-existent user metadata key is noop",
|
|
ctx: Instance.WithAuthorizationToken(OrgCTX, integration.UserTypeIAMOwner),
|
|
dep: func(req *user.SetUserMetadataRequest) {
|
|
req.UserId = Instance.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
Instance.SetUserMetadata(iamOwnerCTX, req.UserId, "key1", "value1")
|
|
},
|
|
req: &user.SetUserMetadataRequest{
|
|
Metadata: []*user.Metadata{
|
|
{Key: "key2", Value: nil}, // delete non-existent key; should be a no-op
|
|
},
|
|
},
|
|
assert: func(t *testing.T, setResponse *user.SetUserMetadataResponse, testStartTime time.Time, getResponse *user.ListUserMetadataResponse) {
|
|
// assert that the set date is not updated, i.e., it is before testStartTime, because no change to the metadata should be made
|
|
assert.True(t, setResponse.GetSetDate().AsTime().Before(testStartTime))
|
|
|
|
expectedMetadata := []*metadata.Metadata{
|
|
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
|
|
}
|
|
assertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
|
|
},
|
|
},
|
|
{
|
|
name: "update and delete user metadata",
|
|
ctx: Instance.WithAuthorizationToken(OrgCTX, integration.UserTypeIAMOwner),
|
|
dep: func(req *user.SetUserMetadataRequest) {
|
|
req.UserId = Instance.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
Instance.SetUserMetadata(iamOwnerCTX, req.UserId, "key1", "value1")
|
|
Instance.SetUserMetadata(iamOwnerCTX, req.UserId, "key2", "value2")
|
|
},
|
|
req: &user.SetUserMetadataRequest{
|
|
Metadata: []*user.Metadata{
|
|
{Key: "key1", Value: nil}, // delete
|
|
{Key: "key2", Value: []byte(base64.StdEncoding.EncodeToString([]byte("updated")))},
|
|
},
|
|
},
|
|
assert: func(t *testing.T, setResponse *user.SetUserMetadataResponse, testStartTime time.Time, getResponse *user.ListUserMetadataResponse) {
|
|
assert.WithinRange(t, setResponse.GetSetDate().AsTime(), testStartTime, time.Now().UTC())
|
|
|
|
expectedMetadata := []*metadata.Metadata{
|
|
{Key: "key2", Value: []byte(base64.StdEncoding.EncodeToString([]byte("updated")))},
|
|
}
|
|
assertMetadataEquals(t, expectedMetadata, getResponse.GetMetadata())
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.dep != nil {
|
|
tt.dep(tt.req)
|
|
}
|
|
testStartTime := time.Now().UTC()
|
|
got, err := Client.SetUserMetadata(tt.ctx, tt.req)
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
Instance.TriggerUserByID(tt.ctx, tt.req.GetUserId())
|
|
|
|
gotMetadataResponse, err := Client.ListUserMetadata(tt.ctx, &user.ListUserMetadataRequest{
|
|
UserId: tt.req.GetUserId(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
tt.assert(t, got, testStartTime, gotMetadataResponse)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestServer_ListUserMetadata(t *testing.T) {
|
|
iamOwnerCTX := Instance.WithAuthorizationToken(OrgCTX, integration.UserTypeIAMOwner)
|
|
|
|
type args struct {
|
|
ctx context.Context
|
|
dep func(context.Context, *user.ListUserMetadataRequest, *user.ListUserMetadataResponse)
|
|
req *user.ListUserMetadataRequest
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want *user.ListUserMetadataResponse
|
|
}{
|
|
{
|
|
name: "missing permission, actual TotalResult count returned",
|
|
args: args{
|
|
ctx: Instance.WithAuthorizationToken(context.Background(), integration.UserTypeNoPermission),
|
|
dep: func(ctx context.Context, request *user.ListUserMetadataRequest, response *user.ListUserMetadataResponse) {
|
|
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
request.UserId = userID
|
|
Instance.SetUserMetadata(iamOwnerCTX, userID, "key1", "value1")
|
|
},
|
|
req: &user.ListUserMetadataRequest{},
|
|
},
|
|
want: &user.ListUserMetadataResponse{
|
|
Pagination: &filter.PaginationResponse{
|
|
TotalResult: 1,
|
|
AppliedLimit: 100,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "list request",
|
|
args: args{
|
|
ctx: iamOwnerCTX,
|
|
dep: func(ctx context.Context, request *user.ListUserMetadataRequest, response *user.ListUserMetadataResponse) {
|
|
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
request.UserId = userID
|
|
metadataResp := Instance.SetUserMetadata(iamOwnerCTX, userID, "key1", "value1")
|
|
|
|
response.Metadata[0] = &metadata.Metadata{
|
|
CreationDate: metadataResp.GetSetDate(),
|
|
ChangeDate: metadataResp.GetSetDate(),
|
|
Key: "key1",
|
|
Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1"))),
|
|
}
|
|
},
|
|
req: &user.ListUserMetadataRequest{},
|
|
},
|
|
want: &user.ListUserMetadataResponse{
|
|
Pagination: &filter.PaginationResponse{
|
|
TotalResult: 1,
|
|
AppliedLimit: 100,
|
|
},
|
|
Metadata: []*metadata.Metadata{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "list request single key",
|
|
args: args{
|
|
ctx: iamOwnerCTX,
|
|
dep: func(ctx context.Context, request *user.ListUserMetadataRequest, response *user.ListUserMetadataResponse) {
|
|
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
request.UserId = userID
|
|
key := "key1"
|
|
response.Metadata[0] = setUserMetadata(iamOwnerCTX, Instance, userID, key, "value1")
|
|
Instance.SetUserMetadata(iamOwnerCTX, userID, "key2", "value2")
|
|
Instance.SetUserMetadata(iamOwnerCTX, userID, "key3", "value3")
|
|
request.Filters[0] = &metadata.MetadataSearchFilter{
|
|
Filter: &metadata.MetadataSearchFilter_KeyFilter{KeyFilter: &metadata.MetadataKeyFilter{Key: key}},
|
|
}
|
|
},
|
|
req: &user.ListUserMetadataRequest{
|
|
Filters: []*metadata.MetadataSearchFilter{{}},
|
|
},
|
|
},
|
|
want: &user.ListUserMetadataResponse{
|
|
Pagination: &filter.PaginationResponse{
|
|
TotalResult: 1,
|
|
AppliedLimit: 100,
|
|
},
|
|
Metadata: []*metadata.Metadata{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "list multiple keys",
|
|
args: args{
|
|
ctx: iamOwnerCTX,
|
|
dep: func(ctx context.Context, request *user.ListUserMetadataRequest, response *user.ListUserMetadataResponse) {
|
|
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
request.UserId = userID
|
|
|
|
response.Metadata[2] = setUserMetadata(iamOwnerCTX, Instance, userID, "key1", "value1")
|
|
response.Metadata[1] = setUserMetadata(iamOwnerCTX, Instance, userID, "key2", "value2")
|
|
response.Metadata[0] = setUserMetadata(iamOwnerCTX, Instance, userID, "key3", "value3")
|
|
},
|
|
req: &user.ListUserMetadataRequest{},
|
|
},
|
|
want: &user.ListUserMetadataResponse{
|
|
Pagination: &filter.PaginationResponse{
|
|
TotalResult: 3,
|
|
AppliedLimit: 100,
|
|
},
|
|
Metadata: []*metadata.Metadata{
|
|
{}, {}, {},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.args.dep != nil {
|
|
tt.args.dep(tt.args.ctx, tt.args.req, tt.want)
|
|
}
|
|
|
|
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(iamOwnerCTX, time.Minute)
|
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
|
got, listErr := Instance.Client.UserV2.ListUserMetadata(tt.args.ctx, tt.args.req)
|
|
require.NoError(ttt, listErr)
|
|
// always first check length, otherwise its failed anyway
|
|
if assert.Len(ttt, got.Metadata, len(tt.want.Metadata)) {
|
|
assert.EqualExportedValues(ttt, got.Metadata, tt.want.Metadata)
|
|
}
|
|
assertPaginationResponse(ttt, tt.want.Pagination, got.Pagination)
|
|
}, retryDuration, tick, "timeout waiting for expected execution result")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestServer_ListUserMetadata_WithPermissionV2(t *testing.T) {
|
|
ensureFeaturePermissionV2Enabled(t, InstancePermissionV2)
|
|
iamOwnerCTX := InstancePermissionV2.WithAuthorizationToken(OrgCTX, integration.UserTypeIAMOwner)
|
|
|
|
type args struct {
|
|
ctx context.Context
|
|
dep func(context.Context, *user.ListUserMetadataRequest, *user.ListUserMetadataResponse)
|
|
req *user.ListUserMetadataRequest
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want *user.ListUserMetadataResponse
|
|
}{
|
|
{
|
|
name: "missing permission, TotalResult set to 0",
|
|
args: args{
|
|
ctx: InstancePermissionV2.WithAuthorizationToken(context.Background(), integration.UserTypeNoPermission),
|
|
dep: func(ctx context.Context, request *user.ListUserMetadataRequest, response *user.ListUserMetadataResponse) {
|
|
userID := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
request.UserId = userID
|
|
InstancePermissionV2.SetUserMetadata(iamOwnerCTX, userID, "key1", "value1")
|
|
},
|
|
req: &user.ListUserMetadataRequest{},
|
|
},
|
|
want: &user.ListUserMetadataResponse{
|
|
Pagination: &filter.PaginationResponse{
|
|
TotalResult: 0,
|
|
AppliedLimit: 100,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "list request",
|
|
args: args{
|
|
ctx: iamOwnerCTX,
|
|
dep: func(ctx context.Context, request *user.ListUserMetadataRequest, response *user.ListUserMetadataResponse) {
|
|
userID := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
request.UserId = userID
|
|
metadataResp := InstancePermissionV2.SetUserMetadata(iamOwnerCTX, userID, "key1", "value1")
|
|
|
|
response.Metadata[0] = &metadata.Metadata{
|
|
CreationDate: metadataResp.GetSetDate(),
|
|
ChangeDate: metadataResp.GetSetDate(),
|
|
Key: "key1",
|
|
Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1"))),
|
|
}
|
|
},
|
|
req: &user.ListUserMetadataRequest{},
|
|
},
|
|
want: &user.ListUserMetadataResponse{
|
|
Pagination: &filter.PaginationResponse{
|
|
TotalResult: 1,
|
|
AppliedLimit: 100,
|
|
},
|
|
Metadata: []*metadata.Metadata{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "list request single key",
|
|
args: args{
|
|
ctx: iamOwnerCTX,
|
|
dep: func(ctx context.Context, request *user.ListUserMetadataRequest, response *user.ListUserMetadataResponse) {
|
|
userID := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
request.UserId = userID
|
|
key := "key1"
|
|
response.Metadata[0] = setUserMetadata(iamOwnerCTX, InstancePermissionV2, userID, key, "value1")
|
|
InstancePermissionV2.SetUserMetadata(iamOwnerCTX, userID, "key2", "value2")
|
|
InstancePermissionV2.SetUserMetadata(iamOwnerCTX, userID, "key3", "value3")
|
|
request.Filters[0] = &metadata.MetadataSearchFilter{
|
|
Filter: &metadata.MetadataSearchFilter_KeyFilter{KeyFilter: &metadata.MetadataKeyFilter{Key: key}},
|
|
}
|
|
},
|
|
req: &user.ListUserMetadataRequest{
|
|
Filters: []*metadata.MetadataSearchFilter{{}},
|
|
},
|
|
},
|
|
want: &user.ListUserMetadataResponse{
|
|
Pagination: &filter.PaginationResponse{
|
|
TotalResult: 1,
|
|
AppliedLimit: 100,
|
|
},
|
|
Metadata: []*metadata.Metadata{
|
|
{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "list multiple keys",
|
|
args: args{
|
|
ctx: iamOwnerCTX,
|
|
dep: func(ctx context.Context, request *user.ListUserMetadataRequest, response *user.ListUserMetadataResponse) {
|
|
userID := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
request.UserId = userID
|
|
|
|
response.Metadata[2] = setUserMetadata(iamOwnerCTX, InstancePermissionV2, userID, "key1", "value1")
|
|
response.Metadata[1] = setUserMetadata(iamOwnerCTX, InstancePermissionV2, userID, "key2", "value2")
|
|
response.Metadata[0] = setUserMetadata(iamOwnerCTX, InstancePermissionV2, userID, "key3", "value3")
|
|
},
|
|
req: &user.ListUserMetadataRequest{},
|
|
},
|
|
want: &user.ListUserMetadataResponse{
|
|
Pagination: &filter.PaginationResponse{
|
|
TotalResult: 3,
|
|
AppliedLimit: 100,
|
|
},
|
|
Metadata: []*metadata.Metadata{
|
|
{}, {}, {},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.args.dep != nil {
|
|
tt.args.dep(tt.args.ctx, tt.args.req, tt.want)
|
|
}
|
|
|
|
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(iamOwnerCTX, time.Minute)
|
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
|
got, listErr := InstancePermissionV2.Client.UserV2.ListUserMetadata(tt.args.ctx, tt.args.req)
|
|
require.NoError(ttt, listErr)
|
|
// always first check length, otherwise its failed anyway
|
|
if assert.Len(ttt, got.Metadata, len(tt.want.Metadata)) {
|
|
assert.EqualExportedValues(ttt, got.Metadata, tt.want.Metadata)
|
|
}
|
|
assertPaginationResponse(ttt, tt.want.Pagination, got.Pagination)
|
|
}, retryDuration, tick, "timeout waiting for expected execution result")
|
|
})
|
|
}
|
|
}
|
|
|
|
func setUserMetadata(ctx context.Context, instance *integration.Instance, userID, key, value string) *metadata.Metadata {
|
|
metadataResp := instance.SetUserMetadata(ctx, userID, key, value)
|
|
return &metadata.Metadata{
|
|
CreationDate: metadataResp.GetSetDate(),
|
|
ChangeDate: metadataResp.GetSetDate(),
|
|
Key: key,
|
|
Value: []byte(base64.StdEncoding.EncodeToString([]byte(value))),
|
|
}
|
|
}
|
|
|
|
func assertPaginationResponse(t *assert.CollectT, expected *filter.PaginationResponse, actual *filter.PaginationResponse) {
|
|
assert.Equal(t, expected.AppliedLimit, actual.AppliedLimit)
|
|
assert.Equal(t, expected.TotalResult, actual.TotalResult)
|
|
}
|
|
|
|
func TestServer_DeleteUserMetadata(t *testing.T) {
|
|
iamOwnerCTX := Instance.WithAuthorizationToken(OrgCTX, integration.UserTypeIAMOwner)
|
|
|
|
tests := []struct {
|
|
name string
|
|
ctx context.Context
|
|
prepare func(request *user.DeleteUserMetadataRequest) (time.Time, time.Time)
|
|
req *user.DeleteUserMetadataRequest
|
|
wantDeletionDate bool
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "empty id",
|
|
ctx: iamOwnerCTX,
|
|
req: &user.DeleteUserMetadataRequest{
|
|
UserId: "",
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "delete, user not existing",
|
|
ctx: iamOwnerCTX,
|
|
req: &user.DeleteUserMetadataRequest{
|
|
UserId: "notexisting",
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "delete",
|
|
ctx: iamOwnerCTX,
|
|
prepare: func(request *user.DeleteUserMetadataRequest) (time.Time, time.Time) {
|
|
testStartTime := time.Now().UTC()
|
|
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
request.UserId = userID
|
|
key := "key1"
|
|
Instance.SetUserMetadata(iamOwnerCTX, userID, key, "value1")
|
|
request.Keys = []string{key}
|
|
return testStartTime, time.Time{}
|
|
},
|
|
req: &user.DeleteUserMetadataRequest{},
|
|
wantDeletionDate: true,
|
|
},
|
|
{
|
|
name: "delete, empty list",
|
|
ctx: iamOwnerCTX,
|
|
prepare: func(request *user.DeleteUserMetadataRequest) (time.Time, time.Time) {
|
|
testStartTime := time.Now().UTC()
|
|
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
request.UserId = userID
|
|
key := "key1"
|
|
Instance.SetUserMetadata(iamOwnerCTX, userID, key, "value1")
|
|
Instance.DeleteUserMetadata(iamOwnerCTX, userID, key)
|
|
return testStartTime, time.Now().UTC()
|
|
},
|
|
req: &user.DeleteUserMetadataRequest{},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "delete, already removed",
|
|
ctx: iamOwnerCTX,
|
|
prepare: func(request *user.DeleteUserMetadataRequest) (time.Time, time.Time) {
|
|
testStartTime := time.Now().UTC()
|
|
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
request.UserId = userID
|
|
key := "key1"
|
|
Instance.SetUserMetadata(iamOwnerCTX, userID, key, "value1")
|
|
Instance.DeleteUserMetadata(iamOwnerCTX, userID, key)
|
|
request.Keys = []string{key}
|
|
return testStartTime, time.Now().UTC()
|
|
},
|
|
req: &user.DeleteUserMetadataRequest{},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "delete, multiple",
|
|
ctx: iamOwnerCTX,
|
|
prepare: func(request *user.DeleteUserMetadataRequest) (time.Time, time.Time) {
|
|
testStartTime := time.Now().UTC()
|
|
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, integration.Email()).GetId()
|
|
request.UserId = userID
|
|
key1 := "key1"
|
|
Instance.SetUserMetadata(iamOwnerCTX, userID, key1, "value1")
|
|
key2 := "key2"
|
|
Instance.SetUserMetadata(iamOwnerCTX, userID, key2, "value1")
|
|
key3 := "key3"
|
|
Instance.SetUserMetadata(iamOwnerCTX, userID, key3, "value1")
|
|
request.Keys = []string{key1, key2, key3}
|
|
return testStartTime, time.Time{}
|
|
},
|
|
req: &user.DeleteUserMetadataRequest{},
|
|
wantDeletionDate: true,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var testStartTime, deletionDate time.Time
|
|
if tt.prepare != nil {
|
|
testStartTime, deletionDate = tt.prepare(tt.req)
|
|
}
|
|
got, err := Instance.Client.UserV2.DeleteUserMetadata(tt.ctx, tt.req)
|
|
if tt.wantErr {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
assert.NoError(t, err)
|
|
assertDeleteProjectResponse(t, testStartTime, deletionDate, tt.wantDeletionDate, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func assertDeleteProjectResponse(t *testing.T, testStartTime, deletionDate time.Time, expectedDeletionDate bool, actualResp *user.DeleteUserMetadataResponse) {
|
|
if expectedDeletionDate {
|
|
if !deletionDate.IsZero() {
|
|
assert.WithinRange(t, actualResp.GetDeletionDate().AsTime(), testStartTime, deletionDate)
|
|
} else {
|
|
assert.WithinRange(t, actualResp.GetDeletionDate().AsTime(), testStartTime, time.Now().UTC())
|
|
}
|
|
} else {
|
|
assert.Nil(t, actualResp.DeletionDate)
|
|
}
|
|
}
|