diff --git a/internal/daemon/controller/handlers/refresh_token.go b/internal/daemon/controller/handlers/list_token.go similarity index 50% rename from internal/daemon/controller/handlers/refresh_token.go rename to internal/daemon/controller/handlers/list_token.go index 9a3659b905..a8ea104972 100644 --- a/internal/daemon/controller/handlers/refresh_token.go +++ b/internal/daemon/controller/handlers/list_token.go @@ -8,42 +8,82 @@ import ( "github.com/hashicorp/boundary/internal/errors" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/hashicorp/boundary/internal/listtoken" "github.com/hashicorp/boundary/internal/types/resource" "github.com/mr-tron/base58" "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" ) -// ParseRefreshToken parses a refresh token from the input, returning +// ParseListToken parses a list token from the input, returning // an error if the parsing fails. -func ParseRefreshToken(ctx context.Context, token string) (*pbs.ListRefreshToken, error) { - const op = "handlers.ParseRefreshToken" +func ParseListToken(ctx context.Context, token string) (*pbs.ListToken, error) { + const op = "handlers.ParseListToken" marshaled, err := base58.Decode(token) if err != nil { return nil, errors.Wrap(ctx, err, op) } - var tok pbs.ListRefreshToken + var tok pbs.ListToken if err := proto.Unmarshal(marshaled, &tok); err != nil { return nil, errors.Wrap(ctx, err, op) } return &tok, nil } -// MarshalRefreshToken marshals a refresh token to its string representation. -func MarshalRefreshToken(ctx context.Context, token *pbs.ListRefreshToken) (string, error) { - const op = "handlers.MarshalRefreshToken" - if token == nil { +// MarshalListToken assembles and marshals a list token to its string representation. +func MarshalListToken(ctx context.Context, token *listtoken.Token, resourceType pbs.ResourceType) (string, error) { + const op = "handlers.MarshalListToken" + switch { + case token == nil: return "", errors.New(ctx, errors.InvalidParameter, op, "token is required") + case resourceType == pbs.ResourceType_RESOURCE_TYPE_UNSPECIFIED: + return "", errors.New(ctx, errors.InvalidParameter, op, "missing resource type") + case token.ResourceType != ListTokenResourceToResource(resourceType): + return "", errors.New(ctx, errors.Internal, op, "list token resource type does not match expected resource type") } - marshaled, err := proto.Marshal(token) + lt := &pbs.ListToken{ + CreateTime: timestamppb.New(token.CreateTime), + ResourceType: resourceType, + GrantsHash: token.GrantsHash, + } + switch st := token.Subtype.(type) { + case *listtoken.PaginationToken: + lt.Token = &pbs.ListToken_PaginationToken{ + PaginationToken: &pbs.PaginationToken{ + LastItemId: st.LastItemId, + LastItemCreateTime: timestamppb.New(st.LastItemCreateTime), + }, + } + case *listtoken.StartRefreshToken: + lt.Token = &pbs.ListToken_StartRefreshToken{ + StartRefreshToken: &pbs.StartRefreshToken{ + PreviousPhaseUpperBound: timestamppb.New(st.PreviousPhaseUpperBound), + PreviousDeletedIdsTime: timestamppb.New(st.PreviousDeletedIdsTime), + }, + } + case *listtoken.RefreshToken: + lt.Token = &pbs.ListToken_RefreshToken{ + RefreshToken: &pbs.RefreshToken{ + PhaseUpperBound: timestamppb.New(st.PhaseUpperBound), + PhaseLowerBound: timestamppb.New(st.PhaseLowerBound), + PreviousDeletedIdsTime: timestamppb.New(st.PreviousDeletedIdsTime), + LastItemId: st.LastItemId, + LastItemUpdateTime: timestamppb.New(st.LastItemUpdateTime), + }, + } + default: + return "", errors.New(ctx, errors.Internal, op, "unexpected list token subtype") + } + marshaled, err := proto.Marshal(lt) if err != nil { return "", errors.Wrap(ctx, err, op) } return base58.Encode(marshaled), nil } -// RefreshTokenResourceToResource translates a protobuf refresh token resource type +// ListTokenResourceToResource translates a protobuf list token resource type // into a useable domain layer boundary resource type. -func RefreshTokenResourceToResource(rt pbs.ResourceType) resource.Type { +func ListTokenResourceToResource(rt pbs.ResourceType) resource.Type { switch rt { case pbs.ResourceType_RESOURCE_TYPE_ACCOUNT: return resource.Account diff --git a/internal/daemon/controller/handlers/list_token_test.go b/internal/daemon/controller/handlers/list_token_test.go new file mode 100644 index 0000000000..c4c72dfb77 --- /dev/null +++ b/internal/daemon/controller/handlers/list_token_test.go @@ -0,0 +1,382 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package handlers_test + +import ( + "context" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/boundary/internal/daemon/controller/handlers" + pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/hashicorp/boundary/internal/listtoken" + "github.com/hashicorp/boundary/internal/types/resource" + "github.com/mr-tron/base58" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func Test_ParseListToken(t *testing.T) { + t.Parallel() + pagToken := &pbs.ListToken{ + CreateTime: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)), + ResourceType: pbs.ResourceType_RESOURCE_TYPE_TARGET, + GrantsHash: []byte("some hash"), + Token: &pbs.ListToken_PaginationToken{ + PaginationToken: &pbs.PaginationToken{ + LastItemId: "ttcp_1234567890", + LastItemCreateTime: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)), + }, + }, + } + pagBytes, err := proto.Marshal(pagToken) + require.NoError(t, err) + pagString := base58.Encode(pagBytes) + + srToken := &pbs.ListToken{ + CreateTime: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)), + ResourceType: pbs.ResourceType_RESOURCE_TYPE_TARGET, + GrantsHash: []byte("some hash"), + Token: &pbs.ListToken_StartRefreshToken{ + StartRefreshToken: &pbs.StartRefreshToken{ + PreviousPhaseUpperBound: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)), + PreviousDeletedIdsTime: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)), + }, + }, + } + srBytes, err := proto.Marshal(srToken) + require.NoError(t, err) + srString := base58.Encode(srBytes) + + rToken := &pbs.ListToken{ + CreateTime: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)), + ResourceType: pbs.ResourceType_RESOURCE_TYPE_TARGET, + GrantsHash: []byte("some hash"), + Token: &pbs.ListToken_RefreshToken{ + RefreshToken: &pbs.RefreshToken{ + PhaseUpperBound: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)), + PhaseLowerBound: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)), + PreviousDeletedIdsTime: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)), + LastItemId: "ttcp_1234567890", + LastItemUpdateTime: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)), + }, + }, + } + rBytes, err := proto.Marshal(rToken) + require.NoError(t, err) + rString := base58.Encode(rBytes) + + tests := []struct { + name string + token string + want *pbs.ListToken + wantErr bool + }{ + { + name: "valid pagination", + token: pagString, + want: pagToken, + }, + { + name: "valid start-refresh", + token: srString, + want: srToken, + }, + { + name: "valid refresh", + token: rString, + want: rToken, + }, + { + name: "empty token", + token: "", + wantErr: true, + }, + { + name: "invalid base58", + token: "not a token", + wantErr: true, + }, + { + name: "invalid proto", + token: base58.Encode([]byte("not a token")), + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := handlers.ParseListToken(context.Background(), tt.token) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Empty(t, cmp.Diff(got, tt.want, protocmp.Transform())) + }) + } +} + +func Test_MarshalListToken(t *testing.T) { + t.Parallel() + fiveDaysAgo := time.Now().AddDate(0, 0, -5) + tokenCreateTime := fiveDaysAgo + lastItemCreateTime := fiveDaysAgo.Add(time.Hour) + lastItemUpdateTime := fiveDaysAgo.Add(2 * time.Hour) + previousPhaseUpperBoundTime := fiveDaysAgo.Add(3 * time.Hour) + previousDeletedIdsTime := fiveDaysAgo.Add(4 * time.Hour) + phaseLowerBound := fiveDaysAgo.Add(5 * time.Hour) + phaseUpperBound := fiveDaysAgo.Add(6 * time.Hour) + + t.Run("valid pagination", func(t *testing.T) { + pagToken := &pbs.ListToken{ + CreateTime: timestamppb.New(tokenCreateTime), + ResourceType: pbs.ResourceType_RESOURCE_TYPE_TARGET, + GrantsHash: []byte("some hash"), + Token: &pbs.ListToken_PaginationToken{ + PaginationToken: &pbs.PaginationToken{ + LastItemId: "ttcp_1234567890", + LastItemCreateTime: timestamppb.New(lastItemCreateTime), + }, + }, + } + pagBytes, err := proto.Marshal(pagToken) + require.NoError(t, err) + pagString := base58.Encode(pagBytes) + domainPagToken, err := listtoken.NewPagination( + context.Background(), + pagToken.CreateTime.AsTime(), + handlers.ListTokenResourceToResource(pagToken.ResourceType), + pagToken.GrantsHash, + pagToken.GetPaginationToken().LastItemId, + pagToken.GetPaginationToken().LastItemCreateTime.AsTime(), + ) + require.NoError(t, err) + + got, err := handlers.MarshalListToken(context.Background(), domainPagToken, pbs.ResourceType_RESOURCE_TYPE_TARGET) + require.NoError(t, err) + require.Empty(t, cmp.Diff(got, pagString, protocmp.Transform())) + }) + t.Run("valid start-refresh", func(t *testing.T) { + srToken := &pbs.ListToken{ + CreateTime: timestamppb.New(tokenCreateTime), + ResourceType: pbs.ResourceType_RESOURCE_TYPE_TARGET, + GrantsHash: []byte("some hash"), + Token: &pbs.ListToken_StartRefreshToken{ + StartRefreshToken: &pbs.StartRefreshToken{ + PreviousPhaseUpperBound: timestamppb.New(previousPhaseUpperBoundTime), + PreviousDeletedIdsTime: timestamppb.New(previousDeletedIdsTime), + }, + }, + } + srBytes, err := proto.Marshal(srToken) + require.NoError(t, err) + srString := base58.Encode(srBytes) + domainSrToken, err := listtoken.NewStartRefresh( + context.Background(), + srToken.CreateTime.AsTime(), + handlers.ListTokenResourceToResource(srToken.ResourceType), + srToken.GrantsHash, + srToken.GetStartRefreshToken().PreviousDeletedIdsTime.AsTime(), + srToken.GetStartRefreshToken().PreviousPhaseUpperBound.AsTime(), + ) + require.NoError(t, err) + + got, err := handlers.MarshalListToken(context.Background(), domainSrToken, pbs.ResourceType_RESOURCE_TYPE_TARGET) + require.NoError(t, err) + require.Empty(t, cmp.Diff(got, srString, protocmp.Transform())) + }) + t.Run("valid refresh", func(t *testing.T) { + rToken := &pbs.ListToken{ + CreateTime: timestamppb.New(tokenCreateTime), + ResourceType: pbs.ResourceType_RESOURCE_TYPE_TARGET, + GrantsHash: []byte("some hash"), + Token: &pbs.ListToken_RefreshToken{ + RefreshToken: &pbs.RefreshToken{ + PhaseUpperBound: timestamppb.New(phaseUpperBound), + PhaseLowerBound: timestamppb.New(phaseLowerBound), + PreviousDeletedIdsTime: timestamppb.New(previousDeletedIdsTime), + LastItemId: "ttcp_1234567890", + LastItemUpdateTime: timestamppb.New(lastItemUpdateTime), + }, + }, + } + rBytes, err := proto.Marshal(rToken) + require.NoError(t, err) + rString := base58.Encode(rBytes) + domainRToken, err := listtoken.NewRefresh( + context.Background(), + tokenCreateTime, + handlers.ListTokenResourceToResource(rToken.ResourceType), + rToken.GrantsHash, + rToken.GetRefreshToken().PreviousDeletedIdsTime.AsTime(), + rToken.GetRefreshToken().PhaseUpperBound.AsTime(), + rToken.GetRefreshToken().PhaseLowerBound.AsTime(), + rToken.GetRefreshToken().LastItemId, + rToken.GetRefreshToken().LastItemUpdateTime.AsTime(), + ) + require.NoError(t, err) + + got, err := handlers.MarshalListToken(context.Background(), domainRToken, pbs.ResourceType_RESOURCE_TYPE_TARGET) + require.NoError(t, err) + require.Empty(t, cmp.Diff(got, rString, protocmp.Transform())) + }) + t.Run("nil token", func(t *testing.T) { + _, err := handlers.MarshalListToken(context.Background(), nil, pbs.ResourceType_RESOURCE_TYPE_TARGET) + require.Error(t, err) + }) + t.Run("invalid token resource type", func(t *testing.T) { + domainPagToken, err := listtoken.NewPagination( + context.Background(), + tokenCreateTime, + resource.Target, + []byte("some hash"), + "ttcp_1234567890", + lastItemCreateTime, + ) + require.NoError(t, err) + _, err = handlers.MarshalListToken(context.Background(), domainPagToken, pbs.ResourceType_RESOURCE_TYPE_UNSPECIFIED) + require.Error(t, err) + }) + t.Run("token resource type mismatch", func(t *testing.T) { + domainPagToken, err := listtoken.NewPagination( + context.Background(), + tokenCreateTime, + resource.Target, + []byte("some hash"), + "ttcp_1234567890", + lastItemCreateTime, + ) + require.NoError(t, err) + _, err = handlers.MarshalListToken(context.Background(), domainPagToken, pbs.ResourceType_RESOURCE_TYPE_SESSION) + require.Error(t, err) + }) + t.Run("invalid token subtype", func(t *testing.T) { + invalidToken := &listtoken.Token{ + CreateTime: tokenCreateTime, + ResourceType: resource.Target, + GrantsHash: []byte("some hash"), + } + _, err := handlers.MarshalListToken(context.Background(), invalidToken, pbs.ResourceType_RESOURCE_TYPE_TARGET) + require.Error(t, err) + }) +} + +func TestListTokenResourceToResource(t *testing.T) { + t.Parallel() + tests := []struct { + name string + rt pbs.ResourceType + want resource.Type + }{ + { + name: "default unknown", + rt: 0, + want: resource.Unknown, + }, + { + name: "account", + rt: pbs.ResourceType_RESOURCE_TYPE_ACCOUNT, + want: resource.Account, + }, + { + name: "auth_method", + rt: pbs.ResourceType_RESOURCE_TYPE_AUTH_METHOD, + want: resource.AuthMethod, + }, + { + name: "auth_token", + rt: pbs.ResourceType_RESOURCE_TYPE_AUTH_TOKEN, + want: resource.AuthToken, + }, + { + name: "credential_library", + rt: pbs.ResourceType_RESOURCE_TYPE_CREDENTIAL_LIBRARY, + want: resource.CredentialLibrary, + }, + { + name: "credential_store", + rt: pbs.ResourceType_RESOURCE_TYPE_CREDENTIAL_STORE, + want: resource.CredentialStore, + }, + { + name: "credential", + rt: pbs.ResourceType_RESOURCE_TYPE_CREDENTIAL, + want: resource.Credential, + }, + { + name: "group", + rt: pbs.ResourceType_RESOURCE_TYPE_GROUP, + want: resource.Group, + }, + { + name: "host_catalog", + rt: pbs.ResourceType_RESOURCE_TYPE_HOST_CATALOG, + want: resource.HostCatalog, + }, + { + name: "host_set", + rt: pbs.ResourceType_RESOURCE_TYPE_HOST_SET, + want: resource.HostSet, + }, + { + name: "host", + rt: pbs.ResourceType_RESOURCE_TYPE_HOST, + want: resource.Host, + }, + { + name: "managed_group", + rt: pbs.ResourceType_RESOURCE_TYPE_MANAGED_GROUP, + want: resource.ManagedGroup, + }, + { + name: "role", + rt: pbs.ResourceType_RESOURCE_TYPE_ROLE, + want: resource.Role, + }, + { + name: "scope", + rt: pbs.ResourceType_RESOURCE_TYPE_SCOPE, + want: resource.Scope, + }, + { + name: "session_recording", + rt: pbs.ResourceType_RESOURCE_TYPE_SESSION_RECORDING, + want: resource.SessionRecording, + }, + { + name: "session", + rt: pbs.ResourceType_RESOURCE_TYPE_SESSION, + want: resource.Session, + }, + { + name: "storage_bucket", + rt: pbs.ResourceType_RESOURCE_TYPE_STORAGE_BUCKET, + want: resource.StorageBucket, + }, + { + name: "target", + rt: pbs.ResourceType_RESOURCE_TYPE_TARGET, + want: resource.Target, + }, + { + name: "user", + rt: pbs.ResourceType_RESOURCE_TYPE_USER, + want: resource.User, + }, + { + name: "worker", + rt: pbs.ResourceType_RESOURCE_TYPE_WORKER, + want: resource.Worker, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := handlers.ListTokenResourceToResource(tt.rt) + require.Empty(t, cmp.Diff(got, tt.want, protocmp.Transform())) + }) + } +} diff --git a/internal/daemon/controller/handlers/refresh_token_test.go b/internal/daemon/controller/handlers/refresh_token_test.go deleted file mode 100644 index 8b51a25741..0000000000 --- a/internal/daemon/controller/handlers/refresh_token_test.go +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package handlers_test - -import ( - "context" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/hashicorp/boundary/internal/daemon/controller/handlers" - pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" - "github.com/hashicorp/boundary/internal/types/resource" - "github.com/mr-tron/base58" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/timestamppb" -) - -func Test_ParseRefreshToken(t *testing.T) { - testToken := &pbs.ListRefreshToken{ - CreatedTime: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)), - ResourceType: pbs.ResourceType_RESOURCE_TYPE_SESSION, - GrantsHash: []byte("some hash"), - LastItemId: "s_1234567890", - LastItemUpdatedTime: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)), - } - tokBytes, err := proto.Marshal(testToken) - require.NoError(t, err) - tokString := base58.Encode(tokBytes) - - tests := []struct { - name string - token string - want *pbs.ListRefreshToken - wantErr bool - }{ - { - name: "valid", - token: tokString, - want: testToken, - }, - { - name: "empty token", - token: "", - wantErr: true, - }, - { - name: "invalid base58", - token: "not a token", - wantErr: true, - }, - { - name: "invalid proto", - token: base58.Encode([]byte("not a token")), - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := handlers.ParseRefreshToken(context.Background(), tt.token) - if tt.wantErr { - require.Error(t, err) - return - } - require.NoError(t, err) - require.Empty(t, cmp.Diff(got, tt.want, protocmp.Transform())) - }) - } -} - -func Test_MarshalRefreshToken(t *testing.T) { - testToken := &pbs.ListRefreshToken{ - CreatedTime: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)), - ResourceType: pbs.ResourceType_RESOURCE_TYPE_SESSION, - GrantsHash: []byte("some hash"), - LastItemId: "s_1234567890", - LastItemUpdatedTime: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)), - } - tokBytes, err := proto.Marshal(testToken) - require.NoError(t, err) - tokString := base58.Encode(tokBytes) - - tests := []struct { - name string - token *pbs.ListRefreshToken - want string - wantErr bool - }{ - { - name: "valid", - token: testToken, - want: tokString, - }, - { - name: "nil token", - token: nil, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := handlers.MarshalRefreshToken(context.Background(), tt.token) - if tt.wantErr { - require.Error(t, err) - return - } - require.NoError(t, err) - require.Empty(t, cmp.Diff(got, tt.want, protocmp.Transform())) - }) - } -} - -func TestRefreshTokenResourceToResource(t *testing.T) { - tests := []struct { - name string - rt pbs.ResourceType - want resource.Type - }{ - { - name: "default unknown", - rt: 0, - want: resource.Unknown, - }, - { - name: "account", - rt: pbs.ResourceType_RESOURCE_TYPE_ACCOUNT, - want: resource.Account, - }, - { - name: "auth_method", - rt: pbs.ResourceType_RESOURCE_TYPE_AUTH_METHOD, - want: resource.AuthMethod, - }, - { - name: "auth_token", - rt: pbs.ResourceType_RESOURCE_TYPE_AUTH_TOKEN, - want: resource.AuthToken, - }, - { - name: "credential_library", - rt: pbs.ResourceType_RESOURCE_TYPE_CREDENTIAL_LIBRARY, - want: resource.CredentialLibrary, - }, - { - name: "credential_store", - rt: pbs.ResourceType_RESOURCE_TYPE_CREDENTIAL_STORE, - want: resource.CredentialStore, - }, - { - name: "credential", - rt: pbs.ResourceType_RESOURCE_TYPE_CREDENTIAL, - want: resource.Credential, - }, - { - name: "group", - rt: pbs.ResourceType_RESOURCE_TYPE_GROUP, - want: resource.Group, - }, - { - name: "host_catalog", - rt: pbs.ResourceType_RESOURCE_TYPE_HOST_CATALOG, - want: resource.HostCatalog, - }, - { - name: "host_set", - rt: pbs.ResourceType_RESOURCE_TYPE_HOST_SET, - want: resource.HostSet, - }, - { - name: "host", - rt: pbs.ResourceType_RESOURCE_TYPE_HOST, - want: resource.Host, - }, - { - name: "managed_group", - rt: pbs.ResourceType_RESOURCE_TYPE_MANAGED_GROUP, - want: resource.ManagedGroup, - }, - { - name: "role", - rt: pbs.ResourceType_RESOURCE_TYPE_ROLE, - want: resource.Role, - }, - { - name: "scope", - rt: pbs.ResourceType_RESOURCE_TYPE_SCOPE, - want: resource.Scope, - }, - { - name: "session_recording", - rt: pbs.ResourceType_RESOURCE_TYPE_SESSION_RECORDING, - want: resource.SessionRecording, - }, - { - name: "session", - rt: pbs.ResourceType_RESOURCE_TYPE_SESSION, - want: resource.Session, - }, - { - name: "storage_bucket", - rt: pbs.ResourceType_RESOURCE_TYPE_STORAGE_BUCKET, - want: resource.StorageBucket, - }, - { - name: "target", - rt: pbs.ResourceType_RESOURCE_TYPE_TARGET, - want: resource.Target, - }, - { - name: "user", - rt: pbs.ResourceType_RESOURCE_TYPE_USER, - want: resource.User, - }, - { - name: "worker", - rt: pbs.ResourceType_RESOURCE_TYPE_WORKER, - want: resource.Worker, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := handlers.RefreshTokenResourceToResource(tt.rt) - require.Empty(t, cmp.Diff(got, tt.want, protocmp.Transform())) - }) - } -}