ContainerImage.Pinniped/internal/registry/whoamirequest/rest_test.go

212 lines
6.1 KiB
Go

// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package whoamirequest
import (
"context"
"errors"
"testing"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/user"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
identityapi "go.pinniped.dev/generated/latest/apis/concierge/identity"
)
func TestNew(t *testing.T) {
r := NewREST(schema.GroupResource{Group: "bears", Resource: "panda"})
require.NotNil(t, r)
require.False(t, r.NamespaceScoped())
require.Equal(t, []string{"pinniped"}, r.Categories())
require.IsType(t, &identityapi.WhoAmIRequest{}, r.New())
require.IsType(t, &identityapi.WhoAmIRequestList{}, r.NewList())
ctx := context.Background()
// check the simple invariants of our no-op list
list, err := r.List(ctx, nil)
require.NoError(t, err)
require.NotNil(t, list)
require.IsType(t, &identityapi.WhoAmIRequestList{}, list)
require.Equal(t, "0", list.(*identityapi.WhoAmIRequestList).ResourceVersion)
require.NotNil(t, list.(*identityapi.WhoAmIRequestList).Items)
require.Len(t, list.(*identityapi.WhoAmIRequestList).Items, 0)
// make sure we can turn lists into tables if needed
table, err := r.ConvertToTable(ctx, list, nil)
require.NoError(t, err)
require.NotNil(t, table)
require.Equal(t, "0", table.ResourceVersion)
require.Nil(t, table.Rows)
// exercise group resource - force error by passing a runtime.Object that does not have an embedded object meta
_, err = r.ConvertToTable(ctx, &metav1.APIGroup{}, nil)
require.Error(t, err, "the resource panda.bears does not support being converted to a Table")
}
func TestCreate(t *testing.T) {
type args struct {
ctx context.Context
obj runtime.Object
createValidation rest.ValidateObjectFunc
options *metav1.CreateOptions
}
tests := []struct {
name string
args args
want runtime.Object
wantErr string
}{
{
name: "wrong type",
args: args{
ctx: genericapirequest.NewContext(),
obj: &metav1.Status{},
createValidation: nil,
options: nil,
},
want: nil,
wantErr: `not a WhoAmIRequest: &v1.Status{TypeMeta:v1.TypeMeta{Kind:"", APIVersion:""}, ListMeta:v1.ListMeta{SelfLink:"", ResourceVersion:"", Continue:"", RemainingItemCount:(*int64)(nil)}, Status:"", Message:"", Reason:"", Details:(*v1.StatusDetails)(nil), Code:0}`,
},
{
name: "bad options",
args: args{
ctx: genericapirequest.NewContext(),
obj: &identityapi.WhoAmIRequest{
TypeMeta: metav1.TypeMeta{
Kind: "SomeKind",
},
ObjectMeta: metav1.ObjectMeta{
Name: "some-name",
},
},
createValidation: nil,
options: &metav1.CreateOptions{DryRun: []string{"stuff"}},
},
want: nil,
wantErr: `SomeKind.identity.concierge.pinniped.dev "some-name" is invalid: dryRun: Unsupported value: []string{"stuff"}`,
},
{
name: "bad namespace",
args: args{
ctx: genericapirequest.WithNamespace(genericapirequest.NewContext(), "some-ns"),
obj: &identityapi.WhoAmIRequest{},
createValidation: nil,
options: nil,
},
want: nil,
wantErr: `namespace is not allowed on WhoAmIRequest: some-ns`,
},
{
// if we add fields to spec, we need additional tests to:
// - make sure admission cannot mutate it
// - the input spec fields are validated correctly
name: "create validation failure",
args: args{
ctx: genericapirequest.NewContext(),
obj: &identityapi.WhoAmIRequest{},
createValidation: func(ctx context.Context, obj runtime.Object) error {
return errors.New("some-error-here")
},
options: nil,
},
want: nil,
wantErr: `some-error-here`,
},
{
name: "no user info",
args: args{
ctx: genericapirequest.NewContext(),
obj: &identityapi.WhoAmIRequest{},
createValidation: nil,
options: nil,
},
want: nil,
wantErr: `Internal error occurred: no user info on request`,
},
{
name: "with user info, no auds",
args: args{
ctx: genericapirequest.WithUser(genericapirequest.NewContext(), &user.DefaultInfo{
Name: "bond",
UID: "007",
Groups: []string{"agents", "ops"},
Extra: map[string][]string{
"fan-of": {"pandas", "twizzlers"},
"needs": {"sleep"},
},
}),
obj: &identityapi.WhoAmIRequest{},
createValidation: nil,
options: nil,
},
want: &identityapi.WhoAmIRequest{
Status: identityapi.WhoAmIRequestStatus{
KubernetesUserInfo: identityapi.KubernetesUserInfo{
User: identityapi.UserInfo{
Username: "bond",
UID: "007",
Groups: []string{"agents", "ops"},
Extra: map[string]identityapi.ExtraValue{
"fan-of": {"pandas", "twizzlers"},
"needs": {"sleep"},
},
},
Audiences: nil,
},
},
},
wantErr: ``,
},
{
name: "with user info and auds",
args: args{
ctx: authenticator.WithAudiences(
genericapirequest.WithUser(genericapirequest.NewContext(), &user.DefaultInfo{
Name: "panda",
}),
authenticator.Audiences{"gitlab", "aws"},
),
obj: &identityapi.WhoAmIRequest{},
createValidation: nil,
options: nil,
},
want: &identityapi.WhoAmIRequest{
Status: identityapi.WhoAmIRequestStatus{
KubernetesUserInfo: identityapi.KubernetesUserInfo{
User: identityapi.UserInfo{
Username: "panda",
},
Audiences: []string{"gitlab", "aws"},
},
},
},
wantErr: ``,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
r := &REST{}
got, err := r.Create(tt.args.ctx, tt.args.obj, tt.args.createValidation, tt.args.options)
require.Equal(t, tt.wantErr, errString(err))
require.Equal(t, tt.want, got)
})
}
}
func errString(err error) string {
if err == nil {
return ""
}
return err.Error()
}