Skip to content

Commit 24f94b0

Browse files
committed
Add basic tests for CLI app initialization and execution
1 parent 177f391 commit 24f94b0

File tree

2 files changed

+87
-33
lines changed

2 files changed

+87
-33
lines changed

cmd/step/main.go

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"errors"
55
"fmt"
6+
"io"
67
"os"
78
"reflect"
89
"regexp"
@@ -15,10 +16,10 @@ import (
1516
"github.com/smallstep/cli-utils/command"
1617
"github.com/smallstep/cli-utils/step"
1718
"github.com/smallstep/cli-utils/ui"
19+
"github.com/smallstep/cli-utils/usage"
1820
"go.step.sm/crypto/jose"
1921
"go.step.sm/crypto/pemutil"
2022

21-
"github.com/smallstep/cli-utils/usage"
2223
"github.com/smallstep/cli/command/version"
2324
"github.com/smallstep/cli/internal/plugin"
2425
"github.com/smallstep/cli/utils"
@@ -66,6 +67,42 @@ func main() {
6667

6768
defer panicHandler()
6869

70+
// create new instance of app
71+
app := newApp(os.Stdout, os.Stderr)
72+
73+
if err := app.Run(os.Args); err != nil {
74+
var messenger interface {
75+
Message() string
76+
}
77+
if errors.As(err, &messenger) {
78+
if os.Getenv("STEPDEBUG") == "1" {
79+
fmt.Fprintf(os.Stderr, "%+v\n\n%s", err, messenger.Message())
80+
} else {
81+
fmt.Fprintln(os.Stderr, messenger.Message())
82+
fmt.Fprintln(os.Stderr, "Re-run with STEPDEBUG=1 for more info.")
83+
}
84+
} else {
85+
if os.Getenv("STEPDEBUG") == "1" {
86+
fmt.Fprintf(os.Stderr, "%+v\n", err)
87+
} else {
88+
fmt.Fprintln(os.Stderr, err)
89+
}
90+
}
91+
//nolint:gocritic // ignore exitAfterDefer error because the defer is required for recovery.
92+
os.Exit(1)
93+
}
94+
}
95+
96+
func newApp(stdout, stderr io.Writer) *cli.App {
97+
// Define default file writers and prompters for go.step.sm/crypto
98+
pemutil.WriteFile = utils.WriteFile
99+
pemutil.PromptPassword = func(msg string) ([]byte, error) {
100+
return ui.PromptPassword(msg)
101+
}
102+
jose.PromptPassword = func(msg string) ([]byte, error) {
103+
return ui.PromptPassword(msg)
104+
}
105+
69106
// Override global framework components
70107
cli.VersionPrinter = func(c *cli.Context) {
71108
version.Command(c)
@@ -111,39 +148,10 @@ func main() {
111148
}
112149

113150
// All non-successful output should be written to stderr
114-
app.Writer = os.Stdout
115-
app.ErrWriter = os.Stderr
116-
117-
// Define default file writers and prompters for go.step.sm/crypto
118-
pemutil.WriteFile = utils.WriteFile
119-
pemutil.PromptPassword = func(msg string) ([]byte, error) {
120-
return ui.PromptPassword(msg)
121-
}
122-
jose.PromptPassword = func(msg string) ([]byte, error) {
123-
return ui.PromptPassword(msg)
124-
}
151+
app.Writer = stdout
152+
app.ErrWriter = stderr
125153

126-
if err := app.Run(os.Args); err != nil {
127-
var messenger interface {
128-
Message() string
129-
}
130-
if errors.As(err, &messenger) {
131-
if os.Getenv("STEPDEBUG") == "1" {
132-
fmt.Fprintf(os.Stderr, "%+v\n\n%s", err, messenger.Message())
133-
} else {
134-
fmt.Fprintln(os.Stderr, messenger.Message())
135-
fmt.Fprintln(os.Stderr, "Re-run with STEPDEBUG=1 for more info.")
136-
}
137-
} else {
138-
if os.Getenv("STEPDEBUG") == "1" {
139-
fmt.Fprintf(os.Stderr, "%+v\n", err)
140-
} else {
141-
fmt.Fprintln(os.Stderr, err)
142-
}
143-
}
144-
//nolint:gocritic // ignore exitAfterDefer error because the defer is required for recovery.
145-
os.Exit(1)
146-
}
154+
return app
147155
}
148156

149157
func panicHandler() {

cmd/step/main_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"regexp"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestAppHasAllCommands(t *testing.T) {
12+
app := newApp(&bytes.Buffer{}, &bytes.Buffer{})
13+
require.NotNil(t, app)
14+
15+
require.Equal(t, "step", app.Name)
16+
require.Equal(t, "step", app.HelpName)
17+
18+
var names = make([]string, 0, len(app.Commands))
19+
for _, c := range app.Commands {
20+
names = append(names, c.Name)
21+
}
22+
require.Equal(t, []string{
23+
"help", "api", "path", "base64", "fileserver",
24+
"certificate", "completion", "context", "crl",
25+
"crypto", "oauth", "version", "ca", "beta", "ssh",
26+
}, names)
27+
}
28+
29+
const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
30+
31+
var ansiRegex = regexp.MustCompile(ansi)
32+
33+
func TestAppRuns(t *testing.T) {
34+
stdout := &bytes.Buffer{}
35+
stderr := &bytes.Buffer{}
36+
37+
app := newApp(stdout, stderr)
38+
require.NotNil(t, app)
39+
40+
err := app.Run([]string{"step"})
41+
require.NoError(t, err)
42+
require.Empty(t, stderr.Bytes())
43+
44+
output := ansiRegex.ReplaceAllString(stdout.String(), "")
45+
require.Contains(t, output, "step -- plumbing for distributed systems")
46+
}

0 commit comments

Comments
 (0)