Connect invocation execution to one reporter
This commit is contained in:
142
execute_reporting_test.go
Normal file
142
execute_reporting_test.go
Normal file
@@ -0,0 +1,142 @@
|
||||
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())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user