package commandkit import ( "bytes" "context" "errors" "strings" "testing" ) func TestDebugEventsFollowModeAndFinalizationOrder(t *testing.T) { cause := errors.New("private operation cause") command := NewCommand(CommandSpec[struct{}]{ Name: "run", Run: func(inv Invocation, _ struct{}) Outcome { if err := inv.Reporter().AtEnd().Info("deferred summary"); err != nil { t.Errorf("queue summary: %v", err) } return Failure("operation failed").WithCause(cause) }, }) app, err := New(Identity{Name: "Example"}, command, Alias("shortcut", "run"), FlagAlias("--trace", "--debug")) if err != nil { t.Fatal(err) } var diagnostics bytes.Buffer outcome := app.Execute(context.Background(), []string{"shortcut", "--verbose", "--trace"}, Streams{Err: &diagnostics}) if outcome.Kind != OutcomeOperational || !errors.Is(outcome.Cause, cause) { t.Fatalf("outcome = %#v", outcome) } got := diagnostics.String() for _, fragment := range []string{ "resolved command alias \"shortcut\" to \"run\"", "selected debug reporting mode using --trace (alias for --debug)", "deferred summary", "handler completed for \"run\" with operational outcome and status 1", "ERROR: operation failed", } { if !strings.Contains(got, fragment) { t.Errorf("diagnostics %q omit %q", got, fragment) } } if strings.Contains(got, cause.Error()) { t.Fatalf("diagnostics exposed the operation cause: %q", got) } ordered := []string{ "resolved command alias", "selected debug reporting mode", "deferred summary", "handler completed", "ERROR: operation failed", } previous := -1 for _, fragment := range ordered { index := strings.Index(got, fragment) if index <= previous { t.Fatalf("diagnostic event %q out of order in %q", fragment, got) } previous = index } } func TestDebugEventsRespectFinalQuietSelection(t *testing.T) { command := NewCommand(CommandSpec[struct{}]{ Name: "run", Run: func(Invocation, struct{}) Outcome { return Success() }, }) app, err := New(Identity{Name: "Example"}, command) if err != nil { t.Fatal(err) } var diagnostics bytes.Buffer outcome := app.Execute(context.Background(), []string{"run", "--debug", "--quiet"}, Streams{Err: &diagnostics}) if outcome.Kind != OutcomeSuccess || diagnostics.Len() != 0 { t.Fatalf("outcome = %#v, diagnostics = %q", outcome, diagnostics.String()) } } func TestDebugCompletionRequiresAHandler(t *testing.T) { command := NewCommand(CommandSpec[struct{}]{ Name: "run", Run: func(Invocation, struct{}) Outcome { return Success() }, }) app, err := New(Identity{Name: "Example"}, command) if err != nil { t.Fatal(err) } var diagnostics bytes.Buffer app.Execute(context.Background(), []string{"unknown", "--debug"}, Streams{Err: &diagnostics}) if strings.Contains(diagnostics.String(), "handler completed") { t.Fatalf("no-handler diagnostics include completion: %q", diagnostics.String()) } } func TestDebugDeliveryFailureAffectsInvocationOutcome(t *testing.T) { ran := false command := NewCommand(CommandSpec[struct{}]{ Name: "run", Run: func(Invocation, struct{}) Outcome { ran = true return Success() }, }) app, err := New(Identity{Name: "Example"}, command) if err != nil { t.Fatal(err) } writeErr := errors.New("debug destination failed") outcome := app.Execute(context.Background(), []string{"run", "--debug"}, Streams{ Err: executeReportingFailureWriter{err: writeErr}, }) if !ran { t.Fatal("debug delivery failure prevented handler execution") } if outcome.Kind != OutcomeOperational || outcome.Status != 1 || !errors.Is(outcome.Presentation.Diagnostics.WriteError, writeErr) { t.Fatalf("outcome = %#v", outcome) } }