Retain validation report delivery errors in outcomes

This commit is contained in:
2026-10-07 16:48:37 +00:00
parent 3ec30d5eb8
commit f866ca98f9
3 changed files with 71 additions and 3 deletions

View File

@@ -639,7 +639,7 @@ func TestConfigValidationYAMLWarningsAndShortWrites(t *testing.T) {
}
outcome = app.Execute(context.Background(), []string{"config", "validate"}, commandkit.Streams{Out: configKitPartialWriter{}, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeOperational || !outcome.ReportIncomplete || !errors.Is(outcome.Cause, io.ErrShortWrite) {
if outcome.Kind != commandkit.OutcomeOperational || !outcome.ReportIncomplete || !errors.Is(outcome.Cause, io.ErrShortWrite) || !errors.Is(outcome.DeliveryError, io.ErrShortWrite) {
t.Fatalf("report short write outcome = %#v", outcome)
}
var delivered bytes.Buffer
@@ -657,3 +657,64 @@ func (configKitPartialWriter) Write(data []byte) (int, error) {
}
return len(data) - 1, nil
}
func TestValidationReportDeliveryErrorsAtEachWriteBoundary(t *testing.T) {
reportErr := errors.New("report failed")
diagnosticErr := errors.New("diagnostic failed")
app, _ := commandkit.New(commandkit.Identity{Name: "example"})
acquire := func(context.Context, commandkit.ConfigRequest, struct{}) (commandkit.Acquired[commandkit.ConfigView], error) {
return commandkit.Acquired[commandkit.ConfigView]{}, nil
}
if err := commandkit.RegisterConfig(app, commandkit.ConfigSuite[struct{}]{Scopes: []commandkit.ConfigScope[struct{}]{
{Name: "app", Operations: []commandkit.ConfigOperation{commandkit.ConfigValidate}, Acquire: acquire},
{Name: "promptkit", Operations: []commandkit.ConfigOperation{commandkit.ConfigValidate}, Acquire: acquire},
}}); err != nil {
t.Fatal(err)
}
for _, format := range []string{"text", "yaml"} {
writes := 2
if format == "yaml" {
writes = 3
}
for at := 1; at <= writes; at++ {
for _, brokenErr := range []bool{false, true} {
out := &validationBoundaryWriter{failAt: at, err: reportErr}
errStream := io.Writer(io.Discard)
if brokenErr {
errStream = failingWriter{diagnosticErr}
}
result := app.Execute(context.Background(), []string{"config", "validate", "--scope", "all", "--format", format}, commandkit.Streams{Out: out, Err: errStream})
if result.Kind != commandkit.OutcomeOperational || !result.ReportIncomplete || !errors.Is(result.Cause, reportErr) || !errors.Is(result.DeliveryError, reportErr) || (brokenErr && !errors.Is(result.DeliveryError, diagnosticErr)) {
t.Fatalf("format=%s at=%d brokenErr=%t result=%#v", format, at, brokenErr, result)
}
if at > 1 && out.written.Len() == 0 {
t.Fatal("lost completed prefix")
}
}
}
}
failedApp, _ := commandkit.New(commandkit.Identity{Name: "example"})
if err := commandkit.RegisterConfig(failedApp, commandkit.ConfigSuite[struct{}]{Scopes: []commandkit.ConfigScope[struct{}]{{Name: "app", Operations: []commandkit.ConfigOperation{commandkit.ConfigValidate}, Acquire: func(context.Context, commandkit.ConfigRequest, struct{}) (commandkit.Acquired[commandkit.ConfigView], error) {
return commandkit.Acquired[commandkit.ConfigView]{}, reportErr
}}}}); err != nil {
t.Fatal(err)
}
result := failedApp.Execute(context.Background(), []string{"config", "validate"}, commandkit.Streams{})
if !errors.Is(result.Cause, reportErr) || result.DeliveryError != nil {
t.Fatalf("acquisition failure=%#v", result)
}
}
type validationBoundaryWriter struct {
failAt, calls int
err error
written bytes.Buffer
}
func (w *validationBoundaryWriter) Write(p []byte) (int, error) {
w.calls++
if w.calls == w.failAt {
return 0, w.err
}
return w.written.Write(p)
}