client/cmd/kn/main_test.go

304 lines
7.5 KiB
Go

// Copyright © 2018 The Knative Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"errors"
"fmt"
"os"
"strings"
"testing"
"github.com/spf13/cobra"
"gotest.tools/assert"
"knative.dev/client/lib/test"
"knative.dev/client/pkg/kn/config"
"knative.dev/client/pkg/kn/root"
"knative.dev/client/pkg/util"
)
func TestValidatePlugin(t *testing.T) {
// Build up simple command hierarchy
root := cobra.Command{}
one := &cobra.Command{Use: "one"}
one.AddCommand(&cobra.Command{Use: "eins"}, &cobra.Command{Use: "zwei"})
two := &cobra.Command{Use: "two"}
root.AddCommand(one, two)
data := []struct {
givenPluginCommandParts []string
expectedErrors []string
}{
{
// Allowed because it add a new top-level plugin
[]string{"test"},
nil,
},
{
// Allowed because it adds to an existing command-group
[]string{"one", "drei"},
nil,
},
{
// Forbidden because it overrides an command-group
[]string{"one"},
[]string{"pluginPath", "one"},
},
{
// Forbidden because it overrides a leaf-command
[]string{"one", "zwei"},
[]string{"pluginPath", "one", "zwei"},
},
{
// Forbidden because it would mis-use a leaf-comman to a command-group
[]string{"one", "zwei", "trois"},
[]string{"pluginPath", "one", "zwei"},
},
{
// Forbidden because it overrides a (top-level) leaf-command
[]string{"two"},
[]string{"pluginPath", "two"},
},
{
// Forbidden because it would add to a leaf command
[]string{"two", "deux", "and", "more"},
[]string{"pluginPath", "two", "deux"},
},
}
for i, d := range data {
step := fmt.Sprintf("Check %d", i)
err := validatePlugin(&root, commandPartsOnlyPlugin(d.givenPluginCommandParts))
if len(d.expectedErrors) == 0 {
assert.NilError(t, err, step)
} else {
assert.Assert(t, err != nil, step)
assert.Assert(t, util.ContainsAll(err.Error(), d.expectedErrors...), step)
}
}
}
// Used above for wrapping the command part to check
type commandPartsOnlyPlugin []string
func (f commandPartsOnlyPlugin) CommandParts() []string { return f }
func (f commandPartsOnlyPlugin) Name() string { return "" }
func (f commandPartsOnlyPlugin) Execute(args []string) error { return nil }
func (f commandPartsOnlyPlugin) Description() (string, error) { return "", nil }
func (f commandPartsOnlyPlugin) Path() string { return "pluginPath" }
func TestArgsWithoutCommands(t *testing.T) {
data := []struct {
givenCmdArgs []string
givenPluginCommandParts []string
expectedResult []string
}{
{
[]string{"--option", "val", "one", "second", "rest"},
[]string{"one", "second"},
[]string{"--option", "val", "rest"},
},
{
[]string{"--option", "val", "one", "second", "rest"},
[]string{"second", "one"},
[]string{"--option", "val", "one", "rest"},
},
{
[]string{"--option", "val", "one", "second", "third", "one", "rest"},
[]string{"second", "one"},
[]string{"--option", "val", "one", "third", "rest"},
},
}
for _, d := range data {
result := argsWithoutCommands(d.givenCmdArgs, d.givenPluginCommandParts)
assert.DeepEqual(t, result, d.expectedResult)
}
}
func TestUnknownCommands(t *testing.T) {
oldArgs := os.Args
defer (func() {
os.Args = oldArgs
})()
data := []struct {
givenCmdArgs []string
commandPath []string
expectedError []string
}{
{
[]string{"service", "unknown", "test", "--scale-min=0"},
[]string{"service"},
[]string{"unknown sub-command", "unknown"},
},
{
[]string{"service", "--foo=bar"},
[]string{"service"},
[]string{},
},
{
[]string{"source", "ping", "blub", "--foo=bar"},
[]string{"source", "ping"},
[]string{"unknown sub-command", "blub"},
},
}
for _, d := range data {
args := append([]string{"kn"}, d.givenCmdArgs...)
rootCmd, err := root.NewRootCommand(nil)
rootCmd.FParseErrWhitelist = cobra.FParseErrWhitelist{UnknownFlags: true}
os.Args = args
assert.NilError(t, err)
err = validateRootCommand(rootCmd)
if len(d.expectedError) == 0 {
assert.NilError(t, err)
continue
}
assert.Assert(t, err != nil)
assert.Assert(t, util.ContainsAll(err.Error(), d.expectedError...))
cmd, _, e := rootCmd.Find(d.commandPath)
assert.NilError(t, e)
for _, sub := range cmd.Commands() {
assert.ErrorContains(t, err, sub.Name())
}
}
}
func TestStripFlags(t *testing.T) {
data := []struct {
givenArgs []string
expectedCommands []string
expectedError string
}{
{
[]string{"test", "-h", "second", "--bla"},
[]string{"test", "second"},
"",
},
{
[]string{"--help", "test"},
[]string{"test"},
"",
},
{
[]string{"--unknown-option", "bla", "test", "second"},
[]string{"test", "second"},
"",
},
{
[]string{"--lookup-plugins", "bla", "test", "second"},
[]string{"bla", "test", "second"},
"",
},
{
[]string{"--config-file", "bla", "test", "second"},
[]string{"test", "second"},
"",
},
{
[]string{"test"},
[]string{"test"},
"",
},
}
for i, f := range data {
step := fmt.Sprintf("Check %d", i)
cmd := &cobra.Command{FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true}}
config.AddBootstrapFlags(cmd.Flags())
commands, err := stripFlags(cmd, f.givenArgs)
assert.DeepEqual(t, commands, f.expectedCommands)
if f.expectedError != "" {
assert.ErrorContains(t, err, f.expectedError, step)
} else {
assert.NilError(t, err, step)
}
}
t.Log("checking error case for stripFlags")
cmd, err := root.NewRootCommand(nil)
assert.NilError(t, err)
_, err = stripFlags(cmd, []string{"--config"})
assert.ErrorContains(t, err, "needs an argument")
}
func TestRunWithError(t *testing.T) {
data := []struct {
given string
expected string
}{
{
"unknown sub-command blub",
"Error: unknown sub-command blub",
},
{
"error: unknown type blub",
"Error: unknown type blub",
},
}
for _, d := range data {
capture := test.CaptureOutput(t)
printError(errors.New(d.given))
stdOut, errOut := capture.Close()
assert.Equal(t, stdOut, "")
assert.Assert(t, strings.Contains(errOut, d.expected))
assert.Assert(t, util.ContainsAll(errOut, "Run", "--help", "usage"))
}
}
func TestRunWithPluginError(t *testing.T) {
data := []struct {
given string
expected string
}{
{
"exit status 1",
"Error: exit status 1",
},
}
for _, d := range data {
capture := test.CaptureOutput(t)
// displayHelp argument is false for plugin error
printError(&runError{errors.New(d.given)})
stdOut, errOut := capture.Close()
assert.Equal(t, stdOut, "")
assert.Assert(t, strings.Contains(errOut, d.expected))
// check that --help message isn't displayed
assert.Assert(t, util.ContainsNone(errOut, "Run", "--help", "usage"))
}
}
// Smoke test
func TestRun(t *testing.T) {
oldArgs := os.Args
os.Args = []string{"kn", "--config", "/no/config/please.yaml", "version"}
defer (func() {
os.Args = oldArgs
})()
capture := test.CaptureOutput(t)
err := run(os.Args[1:])
out, _ := capture.Close()
assert.NilError(t, err)
assert.Assert(t, util.ContainsAllIgnoreCase(out, "version", "build", "git"))
}