Skip to content

Commit

Permalink
Add unit tests
Browse files Browse the repository at this point in the history
Signed-off-by: Anuj Chaudhari <[email protected]>
  • Loading branch information
anujc25 committed Oct 5, 2024
1 parent efd4ef4 commit 5bde1ca
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 2 deletions.
2 changes: 1 addition & 1 deletion pkg/auth/uaa/tanzu.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func getIssuerEndpoints(issuerURL string) common.IssuerEndPoints {
}
}

func TanzuLogin(issuerURL string, opts ...common.LoginOption) (*common.Token, error) {
var TanzuLogin = func(issuerURL string, opts ...common.LoginOption) (*common.Token, error) {
issuerEndpoints := getIssuerEndpoints(issuerURL)

h := common.NewTanzuLoginHandler(issuerURL, issuerEndpoints.AuthURL, issuerEndpoints.TokenURL, TanzuCLIClientID, tanzuCLIClientSecret, defaultListenAddress, defaultCallbackPath, config.UAAIdpType, nil, nil, term.IsTerminal)
Expand Down
3 changes: 2 additions & 1 deletion pkg/command/apitoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/vmware-tanzu/tanzu-cli/pkg/auth/uaa"
"github.com/vmware-tanzu/tanzu-cli/pkg/cli"
"github.com/vmware-tanzu/tanzu-cli/pkg/constants"
timeutils "github.com/vmware-tanzu/tanzu-cli/pkg/utils/time"
)

func newAPITokenCmd() *cobra.Command {
Expand Down Expand Up @@ -80,7 +81,7 @@ func createAPIToken(cmd *cobra.Command, _ []string) (err error) {

fmt.Fprint(cmd.OutOrStdout(), bold.Sprint("==\n\n"))
fmt.Fprintf(cmd.OutOrStdout(), "%s Your generated API token is: %s\n\n", bold.Sprint("API Token Generation Successful!"), cyanBold.Sprint(token.RefreshToken))
fmt.Fprintf(cmd.OutOrStdout(), "%s The token will expire on %s.\n", bold.Sprint("Token Expiration:"), bold.Sprint(time.Now().Local().Add(time.Second*time.Duration(token.ExpiresIn))))
fmt.Fprintf(cmd.OutOrStdout(), "%s The token will expire on %s.\n", bold.Sprint("Token Expiration:"), bold.Sprint(timeutils.Now().Local().Add(time.Second*time.Duration(token.ExpiresIn)).Format(("2006-01-02 15:04:05"))))
fmt.Fprint(cmd.OutOrStdout(), "Please copy and save your token securely. Note that you will need to regenerate a new token before expiration time and login again to continue using the CLI.\n\n")
fmt.Fprintf(cmd.OutOrStdout(), "For non-interactive login use the API token as follow: %s\n", cyanBold.Sprint("TANZU_API_TOKEN=<token> tanzu login --endpoint <tanzu-platform-endpoint>"))

Expand Down
204 changes: 204 additions & 0 deletions pkg/command/apitoken_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
// Copyright 2024 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package command

import (
"bytes"
"os"
"path/filepath"
"testing"
"time"

"github.com/google/go-cmp/cmp"
"github.com/otiai10/copy"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"

"github.com/vmware-tanzu/tanzu-plugin-runtime/config"
configtypes "github.com/vmware-tanzu/tanzu-plugin-runtime/config/types"

"github.com/vmware-tanzu/tanzu-cli/pkg/auth/common"
"github.com/vmware-tanzu/tanzu-cli/pkg/auth/uaa"
timeutils "github.com/vmware-tanzu/tanzu-cli/pkg/utils/time"
)

// mockTanzuLogin is a mock implementation of the TanzuLogin function
type mockTanzuLogin struct {
mock.Mock
}

func (m *mockTanzuLogin) TanzuLogin(issuerURL string, opts ...common.LoginOption) (*common.Token, error) {
args := m.Called(issuerURL, opts)
return args.Get(0).(*common.Token), args.Error(1)
}

type mockTimeService struct{}

func (ts *mockTimeService) Now() time.Time {
// Return a mock time
return time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC)
}

func TestCreateAPIToken(t *testing.T) {
var configFile, configFileNG *os.File
var err error

setupEnv := func() {
configFile, err = os.CreateTemp("", "config")
assert.NoError(t, err)

err = copy.Copy(filepath.Join("..", "fakes", "config", "tanzu_config.yaml"), configFile.Name())
assert.NoError(t, err)

os.Setenv("TANZU_CONFIG", configFile.Name())

configFileNG, err = os.CreateTemp("", "config_ng")
assert.NoError(t, err)

os.Setenv("TANZU_CONFIG_NEXT_GEN", configFileNG.Name())
err = copy.Copy(filepath.Join("..", "fakes", "config", "tanzu_config_ng.yaml"), configFileNG.Name())
assert.NoError(t, err)
}

teardownEnv := func() {
os.Unsetenv("TANZU_CONFIG")
os.Unsetenv("TANZU_CONFIG_NEXT_GEN")
os.RemoveAll(configFile.Name())
os.RemoveAll(configFileNG.Name())
}

tests := []struct {
name string
context *configtypes.Context
tanzuLoginErr error
wantErr bool
errMsg string
output string
}{
{
name: "success",
context: &configtypes.Context{
Name: "fakecontext",
ContextType: configtypes.ContextTypeTanzu,
GlobalOpts: &configtypes.GlobalServer{
Auth: configtypes.GlobalServerAuth{
Issuer: "https://example.com",
},
},
AdditionalMetadata: map[string]interface{}{
config.TanzuIdpTypeKey: config.UAAIdpType,
},
},
tanzuLoginErr: nil,
wantErr: false,
output: `==
API Token Generation Successful! Your generated API token is: refresh-token
Token Expiration: The token will expire on 2024-01-01 05:00:00.
Please copy and save your token securely. Note that you will need to regenerate a new token before expiration time and login again to continue using the CLI.
For non-interactive login use the API token as follow: TANZU_API_TOKEN=<token> tanzu login --endpoint <tanzu-platform-endpoint>
`,
},
{
name: "no active context",
context: nil,
tanzuLoginErr: nil,
wantErr: true,
errMsg: "No active context of type `tanzu`. Please login to Tanzu Platform first to generate the API token",
},
{
name: "invalid active context",
context: &configtypes.Context{
Name: "fakecontext",
ContextType: configtypes.ContextTypeTanzu,
AdditionalMetadata: map[string]interface{}{
config.TanzuIdpTypeKey: config.UAAIdpType,
},
},
tanzuLoginErr: nil,
wantErr: true,
errMsg: "Invalid active context of type `tanzu`. Please login to Tanzu Platform first to generate the API token",
},
{
name: "invalid IDP type",
context: &configtypes.Context{
Name: "fakecontext",
ContextType: configtypes.ContextTypeTanzu,
GlobalOpts: &configtypes.GlobalServer{
Auth: configtypes.GlobalServerAuth{
Issuer: "https://example.com",
},
},
AdditionalMetadata: map[string]interface{}{
config.TanzuIdpTypeKey: "other",
},
},
tanzuLoginErr: nil,
wantErr: true,
errMsg: "invalid IDP type for the active context. Only UAA IDP type is supported for generating API token",
},
{
name: "TanzuLogin error",
context: &configtypes.Context{
Name: "fakecontext",
ContextType: configtypes.ContextTypeTanzu,
GlobalOpts: &configtypes.GlobalServer{
Auth: configtypes.GlobalServerAuth{
Issuer: "https://example.com",
},
},
AdditionalMetadata: map[string]interface{}{
config.TanzuIdpTypeKey: config.UAAIdpType,
},
},
tanzuLoginErr: errors.New("TanzuLogin error"),
wantErr: true,
errMsg: "unable to login, TanzuLogin error",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
setupEnv()
defer teardownEnv()

cmd := &cobra.Command{}
outputBuf := &bytes.Buffer{}
cmd.SetOutput(outputBuf)

mockTimeService := &mockTimeService{}
timeutils.Now = mockTimeService.Now
defer func() { timeutils.Now = time.Now }()

mockTanzuLogin := &mockTanzuLogin{}
uaa.TanzuLogin = mockTanzuLogin.TanzuLogin
if tt.context != nil && tt.context.GlobalOpts != nil && tt.context.AdditionalMetadata[config.TanzuIdpTypeKey] == config.UAAIdpType {
mockTanzuLogin.On("TanzuLogin", tt.context.GlobalOpts.Auth.Issuer, mock.Anything).
Return(&common.Token{RefreshToken: "refresh-token", ExpiresIn: 3600}, tt.tanzuLoginErr)
}

if tt.context != nil {
err = config.SetContext(tt.context, true)
assert.NoError(t, err)
}

err := createAPIToken(cmd, nil)

if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
if diff := cmp.Diff(tt.output, outputBuf.String()); diff != "" {
t.Errorf("Unexpected output (-expected, +actual): %s", diff)
}
}

mockTanzuLogin.AssertExpectations(t)
})
}
}
9 changes: 9 additions & 0 deletions pkg/utils/time/time.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright 2024 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

// Package time contains time specific functions and variable to make it to overwrite for unit tests
package time

import "time"

var Now = time.Now

0 comments on commit 5bde1ca

Please sign in to comment.