Treat nil diagnostic writers as no-output entries
This commit is contained in:
@@ -63,6 +63,9 @@ architecture. The issues below affect behavior within the implemented structure.
|
|||||||
- R-06 complete: zero outcomes normalize to success/0 and empty delivery state
|
- R-06 complete: zero outcomes normalize to success/0 and empty delivery state
|
||||||
normalizes to none without altering explicit domain outcomes.
|
normalizes to none without altering explicit domain outcomes.
|
||||||
|
|
||||||
|
- R-07 complete: nil diagnostic writers are no-output entries, do not count as
|
||||||
|
delivery attempts, and cannot suppress cause presentation.
|
||||||
|
|
||||||
## Findings and priority
|
## Findings and priority
|
||||||
|
|
||||||
P1 means a supported invocation is blocked or its report content is corrupted.
|
P1 means a supported invocation is blocked or its report content is corrupted.
|
||||||
|
|||||||
11
execute.go
11
execute.go
@@ -111,11 +111,12 @@ func (a *App) present(outcome Outcome, path []string, streams Streams) Outcome {
|
|||||||
if len(diagnostics) == 0 {
|
if len(diagnostics) == 0 {
|
||||||
return outcome
|
return outcome
|
||||||
}
|
}
|
||||||
|
delivered := false
|
||||||
for _, diagnostic := range diagnostics {
|
for _, diagnostic := range diagnostics {
|
||||||
if diagnostic.Write == nil {
|
if diagnostic.Write == nil {
|
||||||
outcome = deliveryFailure(outcome, errors.New("diagnostic has no writer"))
|
continue
|
||||||
return outcome
|
|
||||||
}
|
}
|
||||||
|
delivered = true
|
||||||
writer := &checkedWriter{writer: streams.Err}
|
writer := &checkedWriter{writer: streams.Err}
|
||||||
writeErr := diagnostic.Write(writer)
|
writeErr := diagnostic.Write(writer)
|
||||||
if writer.failure != nil {
|
if writer.failure != nil {
|
||||||
@@ -129,7 +130,9 @@ func (a *App) present(outcome Outcome, path []string, streams Streams) Outcome {
|
|||||||
return deliveryFailure(outcome, writeErr)
|
return deliveryFailure(outcome, writeErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
outcome.DiagnosticDelivery = DeliveryComplete
|
if delivered {
|
||||||
|
outcome.DiagnosticDelivery = DeliveryComplete
|
||||||
|
}
|
||||||
return outcome
|
return outcome
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +151,7 @@ func (a *App) usageLine(path []string) string {
|
|||||||
|
|
||||||
func hasErrorDiagnostic(diagnostics []Diagnostic) bool {
|
func hasErrorDiagnostic(diagnostics []Diagnostic) bool {
|
||||||
for _, diagnostic := range diagnostics {
|
for _, diagnostic := range diagnostics {
|
||||||
if diagnostic.Severity == configkit.SeverityError {
|
if diagnostic.Write != nil && diagnostic.Severity == configkit.SeverityError {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -469,3 +469,39 @@ func TestExecuteNormalizesOutcomeDefaultsAndPreservesDomainValues(t *testing.T)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNilDiagnosticWritersHaveNoOutputAndDoNotHideCauses(t *testing.T) {
|
||||||
|
cause := errors.New("operation failed")
|
||||||
|
for _, base := range []commandkit.Outcome{commandkit.Success(), commandkit.Domain("needs action", 2, nil), commandkit.Failure(cause)} {
|
||||||
|
base.Diagnostics = []commandkit.Diagnostic{{}, {Severity: configkit.SeverityWarning}, {Severity: configkit.SeverityError}}
|
||||||
|
command, err := commandkit.NewCommand(commandkit.CommandSpec[struct{}]{Name: "run", Run: func(commandkit.Invocation, struct{}) commandkit.Outcome { return base }})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
app, err := commandkit.New(commandkit.Identity{Name: "example"}, command)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var diagnostics bytes.Buffer
|
||||||
|
result := app.Execute(context.Background(), []string{"run"}, commandkit.Streams{Err: &diagnostics})
|
||||||
|
want := commandkit.DeliveryNone
|
||||||
|
if base.Cause != nil {
|
||||||
|
want = commandkit.DeliveryComplete
|
||||||
|
}
|
||||||
|
if result.Kind != base.Kind || result.Status != base.Status || result.Domain != base.Domain || result.Cause != base.Cause || result.DeliveryError != nil || result.DiagnosticDelivery != want {
|
||||||
|
t.Fatalf("result=%#v base=%#v", result, base)
|
||||||
|
}
|
||||||
|
if base.Cause == nil && diagnostics.Len() != 0 {
|
||||||
|
t.Fatalf("unexpected output=%q", diagnostics.String())
|
||||||
|
}
|
||||||
|
if base.Cause != nil && strings.Count(diagnostics.String(), cause.Error()) != 1 {
|
||||||
|
t.Fatalf("cause output=%q", diagnostics.String())
|
||||||
|
}
|
||||||
|
if base.Cause != nil {
|
||||||
|
result = app.Execute(context.Background(), []string{"run"}, commandkit.Streams{Err: failingWriter{io.ErrClosedPipe}})
|
||||||
|
if !errors.Is(result.Cause, cause) || !errors.Is(result.DeliveryError, io.ErrClosedPipe) || result.DiagnosticDelivery != commandkit.DeliveryIncomplete {
|
||||||
|
t.Fatalf("failed delivery=%#v", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user