2026-10-26 14:35:44 +00:00
|
|
|
package commandkit
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestHiddenSimpleDeferredMessagesDoNotAccumulate(t *testing.T) {
|
|
|
|
|
for _, mode := range []reportingMode{modeOrdinary, modeQuiet} {
|
|
|
|
|
t.Run(fmt.Sprint(mode), func(t *testing.T) {
|
|
|
|
|
reporter := newReporter(Streams{})
|
2026-11-07 15:40:01 +00:00
|
|
|
setReporterVisibility(reporter, mode, false, false)
|
2026-10-26 14:35:44 +00:00
|
|
|
for index := 0; index < 256; index++ {
|
|
|
|
|
if err := reporter.AtEnd().Debug(fmt.Sprintf("hidden %d", index), nil); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if err := reporter.AtEnd().Warn("hidden warning"); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// This assertion owns the storage optimization, not a public queue API.
|
|
|
|
|
if len(reporter.state.deferred) != 0 {
|
|
|
|
|
t.Fatalf("retained %d renderer-free hidden messages", len(reporter.state.deferred))
|
|
|
|
|
}
|
|
|
|
|
got := reporter.finalize(Success(), nil)
|
|
|
|
|
if got.Status != 0 || got.Presentation.Diagnostics.State != DeliverySuppressed {
|
|
|
|
|
t.Fatalf("outcome=%#v", got)
|
|
|
|
|
}
|
|
|
|
|
if err := reporter.AtEnd().Debug("late"); !errors.Is(err, ErrReporterClosed) {
|
|
|
|
|
t.Fatalf("closed hidden submission=%v", err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
noContent := newReporter(Streams{})
|
|
|
|
|
_ = noContent.AtEnd().Warn("", nil)
|
|
|
|
|
if got := noContent.finalize(Success(), nil); got.Presentation.Diagnostics.State != DeliveryNone {
|
|
|
|
|
t.Fatalf("empty deferred submission became content: %#v", got)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestHiddenDeferredDetailsRemainDeferredAndCaptured(t *testing.T) {
|
|
|
|
|
var diagnostics bytes.Buffer
|
|
|
|
|
reporter := newReporter(Streams{Err: &diagnostics})
|
2026-11-07 15:40:01 +00:00
|
|
|
setReporterVisibility(reporter, modeQuiet, false, false)
|
2026-10-26 14:35:44 +00:00
|
|
|
failure := errors.New("hidden rendering failed")
|
|
|
|
|
var order []string
|
|
|
|
|
details := []Detail{func(w io.Writer) error {
|
|
|
|
|
order = append(order, "first")
|
|
|
|
|
_, _ = io.WriteString(w, "hidden detail")
|
|
|
|
|
return failure
|
|
|
|
|
}}
|
|
|
|
|
_ = reporter.AtEnd().Warn("hidden", details...)
|
|
|
|
|
details[0] = func(io.Writer) error { t.Error("caller changed captured callback"); return nil }
|
|
|
|
|
_ = reporter.AtEnd().Info("", func(io.Writer) error { order = append(order, "second"); return nil })
|
|
|
|
|
if len(order) != 0 {
|
|
|
|
|
t.Fatal("details ran at submission")
|
|
|
|
|
}
|
|
|
|
|
got := reporter.finalize(Success(), nil)
|
|
|
|
|
if strings.Join(order, ",") != "first,second" || !errors.Is(got.Presentation.Diagnostics.RenderError, failure) || got.Status != 1 {
|
|
|
|
|
t.Fatalf("order=%v outcome=%#v", order, got)
|
|
|
|
|
}
|
|
|
|
|
if strings.Contains(diagnostics.String(), "hidden") || len(reporter.state.deferred) != 0 {
|
|
|
|
|
t.Fatalf("hidden content leaked or queue retained: %q", diagnostics.String())
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-11-07 15:40:01 +00:00
|
|
|
|
|
|
|
|
type gatedDiagnosticWriter struct {
|
|
|
|
|
bytes.Buffer
|
|
|
|
|
entered chan struct{}
|
|
|
|
|
release chan struct{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (w *gatedDiagnosticWriter) Write(p []byte) (int, error) {
|
|
|
|
|
if w.entered != nil {
|
|
|
|
|
close(w.entered)
|
|
|
|
|
w.entered = nil
|
|
|
|
|
<-w.release
|
|
|
|
|
}
|
|
|
|
|
return w.Buffer.Write(p)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestReporterDeferredAcceptanceEndsBeforeDelivery(t *testing.T) {
|
|
|
|
|
writer := &gatedDiagnosticWriter{entered: make(chan struct{}), release: make(chan struct{})}
|
|
|
|
|
entered := writer.entered
|
|
|
|
|
reporter := newReporter(Streams{Err: writer})
|
|
|
|
|
setReporterVisibility(reporter, modeVerbose, false, false)
|
|
|
|
|
deferred := reporter.AtEnd()
|
|
|
|
|
if err := deferred.Warn("accepted"); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
completionEntered, finishCompletion := make(chan struct{}), make(chan struct{})
|
|
|
|
|
done := make(chan Outcome, 1)
|
|
|
|
|
go func() {
|
|
|
|
|
done <- reporter.finalize(Failure("terminal"), func(r *Reporter) {
|
|
|
|
|
close(completionEntered)
|
|
|
|
|
<-finishCompletion
|
|
|
|
|
_ = r.Info("completion")
|
|
|
|
|
})
|
|
|
|
|
}()
|
|
|
|
|
<-entered
|
|
|
|
|
late := make(chan error, 1)
|
|
|
|
|
go func() { late <- deferred.Warn("late") }()
|
|
|
|
|
close(writer.release)
|
|
|
|
|
<-completionEntered
|
|
|
|
|
if err := <-late; !errors.Is(err, ErrReporterClosed) {
|
|
|
|
|
t.Errorf("late submission = %v", err)
|
|
|
|
|
}
|
|
|
|
|
if err := deferred.Debug(""); !errors.Is(err, ErrReporterClosed) {
|
|
|
|
|
t.Errorf("empty hidden submission = %v", err)
|
|
|
|
|
}
|
|
|
|
|
close(finishCompletion)
|
|
|
|
|
outcome := <-done
|
|
|
|
|
if got := writer.String(); got != "WARN: accepted\ncompletion\nERROR: terminal\n" {
|
|
|
|
|
t.Errorf("diagnostics = %q", got)
|
|
|
|
|
}
|
|
|
|
|
if outcome.Presentation.Diagnostics.State != DeliveryComplete {
|
|
|
|
|
t.Fatalf("outcome = %#v", outcome)
|
|
|
|
|
}
|
|
|
|
|
}
|