mirror of https://github.com/databricks/cli.git
Add regression tests for CLI error output (#1566)
## Changes Add regression tests for https://github.com/databricks/cli/issues/1563 We test 2 code paths: - if there is an error, we can print to stderr - if there is a valid output, we can print to stdout We should also consider adding black-box tests that will run the CLI binary as a black box and inspect its output to stderr/stdout. ## Tests Unit tests
This commit is contained in:
parent
8f56ca39a2
commit
25737bbb5d
|
@ -92,9 +92,8 @@ func flagErrorFunc(c *cobra.Command, err error) error {
|
||||||
|
|
||||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||||
func Execute(cmd *cobra.Command) {
|
func Execute(ctx context.Context, cmd *cobra.Command) error {
|
||||||
// TODO: deferred panic recovery
|
// TODO: deferred panic recovery
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
// Run the command
|
// Run the command
|
||||||
cmd, err := cmd.ExecuteContextC(ctx)
|
cmd, err := cmd.ExecuteContextC(ctx)
|
||||||
|
@ -118,7 +117,5 @@ func Execute(cmd *cobra.Command) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
return err
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,9 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/databricks/cli/cmd/root"
|
||||||
|
"github.com/databricks/cli/libs/flags"
|
||||||
|
|
||||||
"github.com/databricks/cli/cmd"
|
"github.com/databricks/cli/cmd"
|
||||||
_ "github.com/databricks/cli/cmd/version"
|
_ "github.com/databricks/cli/cmd/version"
|
||||||
"github.com/databricks/cli/libs/cmdio"
|
"github.com/databricks/cli/libs/cmdio"
|
||||||
|
@ -105,7 +108,12 @@ func (t *cobraTestRunner) registerFlagCleanup(c *cobra.Command) {
|
||||||
// Find target command that will be run. Example: if the command run is `databricks fs cp`,
|
// Find target command that will be run. Example: if the command run is `databricks fs cp`,
|
||||||
// target command corresponds to `cp`
|
// target command corresponds to `cp`
|
||||||
targetCmd, _, err := c.Find(t.args)
|
targetCmd, _, err := c.Find(t.args)
|
||||||
require.NoError(t, err)
|
if err != nil && strings.HasPrefix(err.Error(), "unknown command") {
|
||||||
|
// even if command is unknown, we can proceed
|
||||||
|
require.NotNil(t, targetCmd)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
// Force initialization of default flags.
|
// Force initialization of default flags.
|
||||||
// These are initialized by cobra at execution time and would otherwise
|
// These are initialized by cobra at execution time and would otherwise
|
||||||
|
@ -169,22 +177,28 @@ func (t *cobraTestRunner) RunBackground() {
|
||||||
var stdoutW, stderrW io.WriteCloser
|
var stdoutW, stderrW io.WriteCloser
|
||||||
stdoutR, stdoutW = io.Pipe()
|
stdoutR, stdoutW = io.Pipe()
|
||||||
stderrR, stderrW = io.Pipe()
|
stderrR, stderrW = io.Pipe()
|
||||||
root := cmd.New(t.ctx)
|
ctx := cmdio.NewContext(t.ctx, &cmdio.Logger{
|
||||||
root.SetOut(stdoutW)
|
Mode: flags.ModeAppend,
|
||||||
root.SetErr(stderrW)
|
Reader: bufio.Reader{},
|
||||||
root.SetArgs(t.args)
|
Writer: stderrW,
|
||||||
|
})
|
||||||
|
|
||||||
|
cli := cmd.New(ctx)
|
||||||
|
cli.SetOut(stdoutW)
|
||||||
|
cli.SetErr(stderrW)
|
||||||
|
cli.SetArgs(t.args)
|
||||||
if t.stdinW != nil {
|
if t.stdinW != nil {
|
||||||
root.SetIn(t.stdinR)
|
cli.SetIn(t.stdinR)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register cleanup function to restore flags to their original values
|
// Register cleanup function to restore flags to their original values
|
||||||
// once test has been executed. This is needed because flag values reside
|
// once test has been executed. This is needed because flag values reside
|
||||||
// in a global singleton data-structure, and thus subsequent tests might
|
// in a global singleton data-structure, and thus subsequent tests might
|
||||||
// otherwise interfere with each other
|
// otherwise interfere with each other
|
||||||
t.registerFlagCleanup(root)
|
t.registerFlagCleanup(cli)
|
||||||
|
|
||||||
errch := make(chan error)
|
errch := make(chan error)
|
||||||
ctx, cancel := context.WithCancel(t.ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
// Tee stdout/stderr to buffers.
|
// Tee stdout/stderr to buffers.
|
||||||
stdoutR = io.TeeReader(stdoutR, &t.stdout)
|
stdoutR = io.TeeReader(stdoutR, &t.stdout)
|
||||||
|
@ -197,7 +211,7 @@ func (t *cobraTestRunner) RunBackground() {
|
||||||
|
|
||||||
// Run command in background.
|
// Run command in background.
|
||||||
go func() {
|
go func() {
|
||||||
cmd, err := root.ExecuteContextC(ctx)
|
err := root.Execute(ctx, cli)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Logf("Error running command: %s", err)
|
t.Logf("Error running command: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -230,7 +244,7 @@ func (t *cobraTestRunner) RunBackground() {
|
||||||
// These commands are globals so we have to clean up to the best of our ability after each run.
|
// These commands are globals so we have to clean up to the best of our ability after each run.
|
||||||
// See https://github.com/spf13/cobra/blob/a6f198b635c4b18fff81930c40d464904e55b161/command.go#L1062-L1066
|
// See https://github.com/spf13/cobra/blob/a6f198b635c4b18fff81930c40d464904e55b161/command.go#L1062-L1066
|
||||||
//lint:ignore SA1012 cobra sets the context and doesn't clear it
|
//lint:ignore SA1012 cobra sets the context and doesn't clear it
|
||||||
cmd.SetContext(nil)
|
cli.SetContext(nil)
|
||||||
|
|
||||||
// Make caller aware of error.
|
// Make caller aware of error.
|
||||||
errch <- err
|
errch <- err
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
assert "github.com/databricks/cli/libs/dyn/dynassert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnknownCommand(t *testing.T) {
|
||||||
|
stdout, stderr, err := RequireErrorRun(t, "unknown-command")
|
||||||
|
|
||||||
|
assert.Error(t, err, "unknown command", `unknown command "unknown-command" for "databricks"`)
|
||||||
|
assert.Equal(t, "", stdout.String())
|
||||||
|
assert.Contains(t, stderr.String(), "unknown command")
|
||||||
|
}
|
7
main.go
7
main.go
|
@ -2,11 +2,16 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/databricks/cli/cmd"
|
"github.com/databricks/cli/cmd"
|
||||||
"github.com/databricks/cli/cmd/root"
|
"github.com/databricks/cli/cmd/root"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
root.Execute(cmd.New(context.Background()))
|
ctx := context.Background()
|
||||||
|
err := root.Execute(ctx, cmd.New(ctx))
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue