143 lines
5.3 KiB
Go
143 lines
5.3 KiB
Go
package commandkit
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestExecuteGivesEachInvocationItsReporterAndAppliesSelection(t *testing.T) {
|
|
var reporters []*Reporter
|
|
command := NewCommand(CommandSpec[struct{}]{
|
|
Name: "run",
|
|
Run: func(inv Invocation, _ struct{}) Outcome {
|
|
reporter := inv.Reporter()
|
|
reporters = append(reporters, reporter)
|
|
if _, err := fmt.Fprint(reporter.Output(), "result\n"); err != nil {
|
|
t.Errorf("write result: %v", err)
|
|
}
|
|
if err := reporter.Warn("attention"); err != nil {
|
|
t.Errorf("submit warning: %v", err)
|
|
}
|
|
return Outcome{
|
|
Kind: OutcomeDomain, Domain: "accepted", Status: 0,
|
|
Presentation: PresentationResult{
|
|
Output: DeliveryResult{State: DeliveryIncomplete, WriteError: io.ErrClosedPipe},
|
|
},
|
|
}
|
|
},
|
|
})
|
|
app, err := New(Identity{Name: "Example"}, command)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var quietOut, quietErr bytes.Buffer
|
|
quiet := app.Execute(context.Background(), []string{"run", "--quiet"}, Streams{Out: &quietOut, Err: &quietErr})
|
|
if quiet.Kind != OutcomeDomain || quiet.Status != 0 || quiet.Domain != "accepted" {
|
|
t.Fatalf("quiet outcome = %#v", quiet)
|
|
}
|
|
if quietOut.Len() != 0 || quietErr.Len() != 0 {
|
|
t.Fatalf("quiet output = %q, diagnostics = %q", quietOut.String(), quietErr.String())
|
|
}
|
|
if quiet.Presentation.Output.State != DeliverySuppressed || quiet.Presentation.Diagnostics.State != DeliverySuppressed {
|
|
t.Fatalf("quiet presentation = %#v", quiet.Presentation)
|
|
}
|
|
|
|
var verboseOut, verboseErr bytes.Buffer
|
|
verbose := app.Execute(context.Background(), []string{"run", "--verbose"}, Streams{Out: &verboseOut, Err: &verboseErr})
|
|
if verbose.Kind != OutcomeDomain || verbose.Status != 0 {
|
|
t.Fatalf("verbose outcome = %#v", verbose)
|
|
}
|
|
if verboseOut.String() != "result\n" || verboseErr.String() != "WARN: attention\n" {
|
|
t.Fatalf("verbose output = %q, diagnostics = %q", verboseOut.String(), verboseErr.String())
|
|
}
|
|
if verbose.Presentation.Output.State != DeliveryComplete || verbose.Presentation.Diagnostics.State != DeliveryComplete {
|
|
t.Fatalf("verbose presentation = %#v", verbose.Presentation)
|
|
}
|
|
if len(reporters) != 2 || reporters[0] == nil || reporters[1] == nil || reporters[0] == reporters[1] {
|
|
t.Fatalf("invocation reporters = %#v", reporters)
|
|
}
|
|
if _, err := reporters[0].Output().Write([]byte("late")); !errors.Is(err, ErrReporterClosed) {
|
|
t.Fatalf("write through finalized reporter error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestExecuteQuietUsageKeepsAliasGuidanceVisible(t *testing.T) {
|
|
command := NewCommand(CommandSpec[struct{}]{
|
|
Name: "run",
|
|
Run: func(Invocation, struct{}) Outcome { return Success() },
|
|
})
|
|
app, err := New(Identity{Name: "Example"}, command, Alias("r", "run"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var output, diagnostics bytes.Buffer
|
|
outcome := app.Execute(context.Background(), []string{"r", "--quiet", "--unknown"}, Streams{Out: &output, Err: &diagnostics})
|
|
if outcome.Kind != OutcomeUsage || outcome.Status != 2 {
|
|
t.Fatalf("outcome = %#v", outcome)
|
|
}
|
|
if output.Len() != 0 {
|
|
t.Fatalf("usage output = %q", output.String())
|
|
}
|
|
if got := diagnostics.String(); !strings.Contains(got, "ERROR: unknown flag") || !strings.Contains(got, "Usage: Example r") || !strings.Contains(got, "Alias for: Example run") {
|
|
t.Fatalf("usage diagnostics = %q", got)
|
|
}
|
|
if outcome.Presentation.Diagnostics.State != DeliveryComplete {
|
|
t.Fatalf("diagnostic presentation = %#v", outcome.Presentation.Diagnostics)
|
|
}
|
|
}
|
|
|
|
type executeReportingFailureWriter struct{ err error }
|
|
|
|
func (w executeReportingFailureWriter) Write([]byte) (int, error) { return 0, w.err }
|
|
|
|
func TestExecuteFinalizesEarlyAndIgnoredWriterFailures(t *testing.T) {
|
|
command := NewCommand(CommandSpec[struct{}]{
|
|
Name: "run",
|
|
Run: func(inv Invocation, _ struct{}) Outcome {
|
|
_, _ = inv.Reporter().Output().Write([]byte("report"))
|
|
return Success()
|
|
},
|
|
})
|
|
app, err := New(Identity{Name: "Example"}, command)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
writeErr := errors.New("result destination failed")
|
|
var diagnostics bytes.Buffer
|
|
outcome := app.Execute(context.Background(), []string{"run"}, Streams{
|
|
Out: executeReportingFailureWriter{err: writeErr}, Err: &diagnostics,
|
|
})
|
|
if outcome.Kind != OutcomeOperational || outcome.Status != 1 || !errors.Is(outcome.Presentation.Output.WriteError, writeErr) {
|
|
t.Fatalf("ignored output failure outcome = %#v", outcome)
|
|
}
|
|
if !strings.Contains(diagnostics.String(), "ERROR: Command failed.") {
|
|
t.Fatalf("fallback diagnostic = %q", diagnostics.String())
|
|
}
|
|
|
|
diagnostics.Reset()
|
|
outcome = app.Execute(nil, []string{"run"}, Streams{Err: &diagnostics})
|
|
if outcome.Kind != OutcomeOperational || outcome.Status != 1 || outcome.Cause == nil {
|
|
t.Fatalf("nil context outcome = %#v", outcome)
|
|
}
|
|
if !strings.Contains(diagnostics.String(), "ERROR: invocation context must not be nil\n") {
|
|
t.Fatalf("nil context diagnostic = %q", diagnostics.String())
|
|
}
|
|
|
|
diagnostics.Reset()
|
|
var nilApp *App
|
|
outcome = nilApp.Execute(context.Background(), []string{"run"}, Streams{Err: &diagnostics})
|
|
if outcome.Kind != OutcomeOperational || outcome.Status != 1 || outcome.Cause == nil {
|
|
t.Fatalf("nil app outcome = %#v", outcome)
|
|
}
|
|
if !strings.Contains(diagnostics.String(), "ERROR: cannot execute commands on a nil app\n") {
|
|
t.Fatalf("nil app diagnostic = %q", diagnostics.String())
|
|
}
|
|
}
|