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 7, 2024
1 parent efd4ef4 commit 7d63052
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 13 deletions.
2 changes: 1 addition & 1 deletion docs/cli/commands/tanzu.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The Tanzu CLI

### SEE ALSO

* [tanzu api-token](tanzu_api-token.md) - API Token Generation for the Tanzu Platform
* [tanzu api-token](tanzu_api-token.md) - API Token Generation for Tanzu Platform
* [tanzu completion](tanzu_completion.md) - Output shell completion code
* [tanzu config](tanzu_config.md) - Configuration for the CLI
* [tanzu context](tanzu_context.md) - Configure and manage contexts for the Tanzu CLI
Expand Down
2 changes: 1 addition & 1 deletion docs/cli/commands/tanzu_api-token.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## tanzu api-token

API Token Generation for the Tanzu Platform
API Token Generation for Tanzu Platform

### Options

Expand Down
2 changes: 1 addition & 1 deletion docs/cli/commands/tanzu_api-token_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ tanzu api-token create [flags]

### SEE ALSO

* [tanzu api-token](tanzu_api-token.md) - API Token Generation for the Tanzu Platform
* [tanzu api-token](tanzu_api-token.md) - API Token Generation for Tanzu Platform

3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/gobwas/glob v0.2.3
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/google/gnostic v0.6.9
github.com/google/go-cmp v0.5.9
github.com/google/go-containerregistry v0.15.2
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.0
Expand Down Expand Up @@ -139,7 +140,6 @@ require (
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/certificate-transparency-go v1.1.6 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-github/v50 v50.2.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
Expand Down Expand Up @@ -196,6 +196,7 @@ require (
github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/thales-e-security/pool v0.0.2 // indirect
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,7 @@ github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
Expand Down
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
14 changes: 6 additions & 8 deletions pkg/command/apitoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package command

import (
"fmt"
"time"

"github.com/fatih/color"
"github.com/pkg/errors"
Expand All @@ -24,7 +23,7 @@ import (
func newAPITokenCmd() *cobra.Command {
apiTokenCmd := &cobra.Command{
Use: "api-token",
Short: "API Token Generation for the Tanzu Platform",
Short: "API Token Generation for Tanzu Platform",
Aliases: []string{"apitoken"},
Annotations: map[string]string{
"group": string(plugin.SystemCmdGroup),
Expand Down Expand Up @@ -53,14 +52,14 @@ func newAPITokenCreateCmd() *cobra.Command {
func createAPIToken(cmd *cobra.Command, _ []string) (err error) {
c, err := config.GetActiveContext(types.ContextTypeTanzu)
if err != nil {
return errors.New("No active context of type `tanzu`. Please login to Tanzu Platform first to generate the API token")
return errors.New("No active context of type `tanzu`. Please login to Tanzu Platform first to generate an API token")
}
if c == nil || c.GlobalOpts == nil || c.GlobalOpts.Auth.Issuer == "" {
return errors.New("Invalid active context of type `tanzu`. Please login to Tanzu Platform first to generate the API token")
return errors.New("Invalid active context of type `tanzu`. Please login to Tanzu Platform first to generate an API token")
}
// Make sure it is of type tanzu with tanzuIdpType as `uaa` else return error
if idpType, exist := c.AdditionalMetadata[config.TanzuIdpTypeKey]; !exist || idpType != "uaa" {
return errors.New("invalid IDP type for the active context. Only UAA IDP type is supported for generating API token")
return errors.New("invalid IDP type for the active context. Only UAA IDP type is supported for generating an API token")
}

var token *commonauth.Token
Expand All @@ -80,9 +79,8 @@ 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.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>"))
fmt.Fprintf(cmd.OutOrStdout(), "For non-interactive login use the API token as follow: %s\n\n", cyanBold.Sprint("TANZU_API_TOKEN=<token> tanzu login --endpoint <tanzu-platform-endpoint>"))
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")

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

package command

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

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

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

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
For non-interactive login use the API token as follow: TANZU_API_TOKEN=<token> tanzu login --endpoint <tanzu-platform-endpoint>
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.
`,
},
{
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 an 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 an 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 an 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)

originalTanzuLogin := uaa.TanzuLogin
defer func() {
uaa.TanzuLogin = originalTanzuLogin
}()

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 7d63052

Please sign in to comment.