Skip to content

Commit

Permalink
Adding a feature to control the status code
Browse files Browse the repository at this point in the history
  • Loading branch information
Rican7 committed Aug 12, 2023
1 parent 6f2c46e commit 70572e2
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 6 deletions.
27 changes: 27 additions & 0 deletions error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package lieut

// StatusCodeError represents an error that reports an associated status code.
type StatusCodeError interface {
error

// StatusCode returns the status code of the error, which can be used by an
// app's execution error to know which status code to return.
StatusCode() int
}

type statusCodeError struct {
error

statusCode int
}

// ErrWithStatusCode takes an error and a status code and returns a type that
// satisfies StatusCodeError.
func ErrWithStatusCode(err error, statusCode int) StatusCodeError {
return &statusCodeError{error: err, statusCode: statusCode}
}

// StatusCode returns the status code of the error.
func (e *statusCodeError) StatusCode() int {
return e.statusCode
}
26 changes: 26 additions & 0 deletions error_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package lieut

import (
"errors"
"testing"
)

func TestErrWithStatusCode(t *testing.T) {
errMsg := "test error"
errCode := 107

err := errors.New(errMsg)

statusCodeErr := ErrWithStatusCode(err, errCode)
if statusCodeErr == nil {
t.Fatal("ErrWithStatusCode returned nil")
}

if gotMsg := statusCodeErr.Error(); gotMsg != errMsg {
t.Errorf("err.Error() returned %q, wanted %q", gotMsg, errMsg)
}

if gotCode := statusCodeErr.StatusCode(); gotCode != errCode {
t.Errorf("err.Error() returned %v, wanted %v", gotCode, errCode)
}
}
13 changes: 13 additions & 0 deletions lieut.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ func (a *MultiCommandApp) CommandNames() []string {

// Run takes a context and arguments, runs the expected command, and returns an
// exit code.
//
// If the init function or command Executor returns a StatusCodeError, then the
// returned exit code will match that of the value returned by
// StatusCodeError.StatusCode().
func (a *SingleCommandApp) Run(ctx context.Context, arguments []string) int {
if len(arguments) == 0 {
arguments = os.Args[1:]
Expand All @@ -216,6 +220,10 @@ func (a *SingleCommandApp) Run(ctx context.Context, arguments []string) int {

// Run takes a context and arguments, runs the expected command, and returns an
// exit code.
//
// If the init function or command Executor returns a StatusCodeError, then the
// returned exit code will match that of the value returned by
// StatusCodeError.StatusCode().
func (a *MultiCommandApp) Run(ctx context.Context, arguments []string) int {
if len(arguments) == 0 {
arguments = os.Args[1:]
Expand Down Expand Up @@ -373,6 +381,11 @@ func (a *app) printErr(err error, pad bool) int {

fmt.Fprintf(a.errOut, msgFmt, err)

var statusErr StatusCodeError
if errors.As(err, &statusErr) {
return statusErr.StatusCode()
}

return 1
}

Expand Down
56 changes: 50 additions & 6 deletions lieut_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ func TestSingleCommandApp_Run(t *testing.T) {
exitCode := app.Run(ctx, args)

if exitCode != wantedExitCode {
t.Errorf("app.Run gave %q, wanted %q", exitCode, wantedExitCode)
t.Errorf("app.Run gave %v, wanted %v", exitCode, wantedExitCode)
}

if !initRan {
Expand Down Expand Up @@ -638,7 +638,7 @@ func TestSingleCommandApp_Run_EmptyArgsProvided(t *testing.T) {
expectedArgs := os.Args[1:]

if exitCode := app.Run(context.TODO(), nil); exitCode != 0 {
t.Errorf("app.Run gave non-zero exit code %q", exitCode)
t.Errorf("app.Run gave non-zero exit code %v", exitCode)
}

if capturedArgs[0] != expectedArgs[0] {
Expand Down Expand Up @@ -716,6 +716,17 @@ test vTest (%s/%s)
wantedOut: "",
wantedErrOut: "Error: test init error\n",
},
"initialize returns status code error": {
init: func() error {
return ErrWithStatusCode(errors.New("test init error"), 101)
},

args: []string{"test"},

wantedExitCode: 101,
wantedOut: "",
wantedErrOut: "Error: test init error\n",
},
"execute returns error": {
exec: func(ctx context.Context, arguments []string) error {
return errors.New("test exec error")
Expand All @@ -727,6 +738,17 @@ test vTest (%s/%s)
wantedOut: "",
wantedErrOut: "\nError: test exec error\n",
},
"execute returns status code error": {
exec: func(ctx context.Context, arguments []string) error {
return ErrWithStatusCode(errors.New("test exec error"), 217)
},

args: []string{"test"},

wantedExitCode: 217,
wantedOut: "",
wantedErrOut: "\nError: test exec error\n",
},
} {
t.Run(testName, func(t *testing.T) {
var out, errOut bytes.Buffer
Expand All @@ -740,7 +762,7 @@ test vTest (%s/%s)
exitCode := app.Run(context.TODO(), testData.args)

if exitCode != testData.wantedExitCode {
t.Errorf("app.Run gave %q, wanted %q", exitCode, testData.wantedExitCode)
t.Errorf("app.Run gave %v, wanted %v", exitCode, testData.wantedExitCode)
}

if out.String() != testData.wantedOut {
Expand Down Expand Up @@ -793,7 +815,7 @@ func TestMultiCommandApp_Run(t *testing.T) {
exitCode := app.Run(ctx, args)

if exitCode != wantedExitCode {
t.Errorf("app.Run gave %q, wanted %q", exitCode, wantedExitCode)
t.Errorf("app.Run gave %v, wanted %v", exitCode, wantedExitCode)
}

if !initRan {
Expand Down Expand Up @@ -837,7 +859,7 @@ func TestMultiCommandApp_Run_EmptyArgsProvided(t *testing.T) {
expectedArgs := os.Args[2:]

if exitCode := app.Run(context.TODO(), nil); exitCode != 0 {
t.Errorf("app.Run gave non-zero exit code %q", exitCode)
t.Errorf("app.Run gave non-zero exit code %v", exitCode)
}

if capturedArgs[0] != expectedArgs[0] {
Expand Down Expand Up @@ -972,6 +994,17 @@ test vTest (%s/%s)
wantedOut: "",
wantedErrOut: "Error: test init error\n",
},
"initialize returns status code error": {
init: func() error {
return ErrWithStatusCode(errors.New("test init error"), 101)
},

args: []string{testCommandInfo.Name},

wantedExitCode: 101,
wantedOut: "",
wantedErrOut: "Error: test init error\n",
},
"execute returns error": {
exec: func(ctx context.Context, arguments []string) error {
return errors.New("test exec error")
Expand All @@ -983,6 +1016,17 @@ test vTest (%s/%s)
wantedOut: "",
wantedErrOut: "\nError: test exec error\n",
},
"execute returns status code error": {
exec: func(ctx context.Context, arguments []string) error {
return ErrWithStatusCode(errors.New("test exec error"), 217)
},

args: []string{testCommandInfo.Name},

wantedExitCode: 217,
wantedOut: "",
wantedErrOut: "\nError: test exec error\n",
},
"unknown command": {
args: []string{"thiscommanddoesnotexist"},

Expand Down Expand Up @@ -1019,7 +1063,7 @@ test vTest (%s/%s)
exitCode := app.Run(context.TODO(), testData.args)

if exitCode != testData.wantedExitCode {
t.Errorf("app.Run gave %q, wanted %q", exitCode, testData.wantedExitCode)
t.Errorf("app.Run gave %v, wanted %v", exitCode, testData.wantedExitCode)
}

if out.String() != testData.wantedOut {
Expand Down

0 comments on commit 70572e2

Please sign in to comment.