Files
zitadel/internal/api/grpc/user/v2/integration_test/metadata_test.go
T
247e278505 feat: delete metadata on SetUserMetadata api (#11776)
# 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]>
2026-03-06 14:57:52 +01:00

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)
}
}