ContainerImage.Pinniped/internal/tokenclient/tokenclient_test.go

203 lines
5.5 KiB
Go
Raw Normal View History

// Copyright 2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package tokenclient
import (
"bytes"
"errors"
"testing"
"time"
"github.com/stretchr/testify/require"
authenticationv1 "k8s.io/api/authentication/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/utils/clock"
clocktesting "k8s.io/utils/clock/testing"
"go.pinniped.dev/internal/plog"
)
func TestNew(t *testing.T) {
mockWhatToDoWithTokenFunc := *new(WhatToDoWithTokenFunc)
mockClient := new(kubernetes.Clientset)
mockTime := time.Now()
mockClock := clocktesting.NewFakeClock(mockTime)
var log bytes.Buffer
testLogger := plog.TestLogger(t, &log)
type args struct {
namespace string
serviceAccountName string
k8sClient *kubernetes.Clientset
whatToDoWithToken WhatToDoWithTokenFunc
logger plog.Logger
opts []Opt
}
tests := []struct {
name string
args args
expected *TokenClient
}{
{
name: "defaults",
args: args{
namespace: "namespace",
serviceAccountName: "serviceAccountName",
k8sClient: mockClient,
whatToDoWithToken: mockWhatToDoWithTokenFunc,
logger: testLogger,
},
expected: &TokenClient{
namespace: "namespace",
serviceAccountName: "serviceAccountName",
k8sClient: mockClient,
whatToDoWithToken: mockWhatToDoWithTokenFunc,
expirationSeconds: 600,
clock: clock.RealClock{},
logger: testLogger,
},
},
{
name: "with all opts",
args: args{
namespace: "custom-namespace",
serviceAccountName: "custom-serviceAccountName",
k8sClient: mockClient,
whatToDoWithToken: mockWhatToDoWithTokenFunc,
logger: testLogger,
opts: []Opt{
WithExpirationSeconds(777),
withClock(mockClock),
},
},
expected: &TokenClient{
namespace: "custom-namespace",
serviceAccountName: "custom-serviceAccountName",
k8sClient: mockClient,
whatToDoWithToken: mockWhatToDoWithTokenFunc,
expirationSeconds: 777,
clock: mockClock,
logger: testLogger,
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
actual := New(
tt.args.namespace,
tt.args.serviceAccountName,
tt.args.k8sClient,
tt.args.whatToDoWithToken,
tt.args.logger,
tt.args.opts...,
)
require.Equal(t, tt.expected, actual)
})
}
}
// withClock should only be used for testing.
func withClock(clock clock.Clock) Opt {
return func(client *TokenClient) {
client.clock = clock
}
}
func TestFetchToken(t *testing.T) {
mockTime := metav1.Now()
type expected struct {
tokenRequestStatus authenticationv1.TokenRequestStatus
ttl metav1.Duration
errMessage string
}
tests := []struct {
name string
expirationSeconds int64
howToFetchTokenFromAPIServer howToFetchTokenFromAPIServer
expected expected
}{
{
name: "happy path",
expirationSeconds: 555,
howToFetchTokenFromAPIServer: func(_ *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error) {
tokenRequest := authenticationv1.TokenRequest{
Status: authenticationv1.TokenRequestStatus{
Token: "token value",
ExpirationTimestamp: metav1.NewTime(mockTime.Add(25 * time.Minute)),
},
}
return &tokenRequest, nil
},
expected: expected{
tokenRequestStatus: authenticationv1.TokenRequestStatus{
Token: "token value",
ExpirationTimestamp: metav1.NewTime(mockTime.Add(25 * time.Minute)),
},
ttl: metav1.Duration{
Duration: 25 * time.Minute,
},
},
},
{
name: "returns errors from howToFetchTokenFromAPIServer",
expirationSeconds: 444,
howToFetchTokenFromAPIServer: func(_ *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error) {
return nil, errors.New("has an error")
},
expected: expected{
errMessage: "error creating token: has an error",
},
},
{
name: "errors when howToFetchTokenFromAPIServer returns nil",
expirationSeconds: 333,
howToFetchTokenFromAPIServer: func(_ *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error) {
return nil, nil
},
expected: expected{
errMessage: "tokenRequest is nil after request",
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
wrappedFunc := func(tokenRequest *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error) {
require.NotNil(t, tokenRequest)
require.Equal(t, tt.expirationSeconds, *tokenRequest.Spec.ExpirationSeconds)
require.Empty(t, tokenRequest.Spec.Audiences)
require.Empty(t, tokenRequest.Spec.BoundObjectRef)
return tt.howToFetchTokenFromAPIServer(tokenRequest)
}
mockClock := clocktesting.NewFakeClock(mockTime.Time)
var log bytes.Buffer
tokenClient := TokenClient{
expirationSeconds: tt.expirationSeconds,
clock: mockClock,
logger: plog.TestLogger(t, &log),
}
tokenRequestStatus, ttl, err := tokenClient.fetchToken(
wrappedFunc,
)
if tt.expected.errMessage != "" {
require.ErrorContains(t, err, tt.expected.errMessage)
} else {
require.Equal(t, tt.expected.tokenRequestStatus, tokenRequestStatus)
require.Equal(t, tt.expected.ttl, ttl)
}
})
}
}