Separate show cancellation from rendering errors
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Consolidated audit catalog
|
# Consolidated audit catalog
|
||||||
|
|
||||||
Status: **23 distinct open findings: one P2 and 22 P3**, consolidated from 30
|
Status: **22 distinct open findings: 22 P3**, consolidated from 30
|
||||||
numbered entries in seven audit documents. Reviewed audit state:
|
numbered entries in seven audit documents. Reviewed audit state:
|
||||||
`db732e3a1de55c844f77870e1c6db683c2685d5d`.
|
`db732e3a1de55c844f77870e1c6db683c2685d5d`.
|
||||||
|
|
||||||
@@ -122,6 +122,11 @@ Also noted in the [documentation cross-references](13-documentation.md#previousl
|
|||||||
|
|
||||||
## F05 — P2: Configuration-show cancellation is also recorded as a rendering failure
|
## F05 — P2: Configuration-show cancellation is also recorded as a rendering failure
|
||||||
|
|
||||||
|
**Complete.** A cancellation checkpoint stops buffered show preparation without
|
||||||
|
reporting cancellation as a render error. A regression failed before the fix;
|
||||||
|
focused tests now verify no partial output, cancellation identity, and retained
|
||||||
|
native rendering errors when both occur.
|
||||||
|
|
||||||
ckconfig checks cancellation during buffered show preparation inside the
|
ckconfig checks cancellation during buffered show preparation inside the
|
||||||
`RenderOutput` callback. Returning that cancellation from the renderer records
|
`RenderOutput` callback. Returning that cancellation from the renderer records
|
||||||
both the correct operation cause and a false presentation failure, conflating
|
both the correct operation cause and a false presentation failure, conflating
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Audit remediation implementation plan
|
# Audit remediation implementation plan
|
||||||
|
|
||||||
Status: in progress; Stages 1–4 complete.
|
Status: in progress; Stages 1–5 complete.
|
||||||
|
|
||||||
Baseline: `d492c7747175c01a53261206c2b4356e53453621`. Before this sprint,
|
Baseline: `d492c7747175c01a53261206c2b4356e53453621`. Before this sprint,
|
||||||
intervening changes recorded decisions and retired the completed older
|
intervening changes recorded decisions and retired the completed older
|
||||||
@@ -223,6 +223,12 @@ Complete when empty successful acquisition cannot masquerade as prepared preview
|
|||||||
|
|
||||||
## Stage 5 — Separate show preparation cancellation from rendering failures
|
## Stage 5 — Separate show preparation cancellation from rendering failures
|
||||||
|
|
||||||
|
**Complete.** Buffered show preparation carries cancellation outside managed
|
||||||
|
rendering without emitting the incomplete report. The two-scope regression
|
||||||
|
failed against the original behavior and now passes, including simultaneous
|
||||||
|
native rendering failure; focused show, buffered cancellation and write-failure
|
||||||
|
tests passed in the offline aligned Go 1.27.0 workspace with `-mod=readonly`.
|
||||||
|
|
||||||
Findings: F05. Source: ckconfig `show.go`; reference `operations.go`'s
|
Findings: F05. Source: ckconfig `show.go`; reference `operations.go`'s
|
||||||
renderPreparedConfigReport checkpoint pattern.
|
renderPreparedConfigReport checkpoint pattern.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ckconfig
|
package ckconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
@@ -67,3 +68,39 @@ func TestBufferedReportDoesNotRejectCancellationAfterDeliveryStarts(t *testing.T
|
|||||||
t.Fatalf("post-delivery cancellation changed the operation outcome: %#v", got)
|
t.Fatalf("post-delivery cancellation changed the operation outcome: %#v", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type effectiveWriterFunc func(io.Writer) error
|
||||||
|
|
||||||
|
func (write effectiveWriterFunc) WriteEffective(w io.Writer) error { return write(w) }
|
||||||
|
|
||||||
|
func TestShowCancellationDuringBufferedPreparation(t *testing.T) {
|
||||||
|
renderErr := errors.New("native rendering failed")
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
renderErr error
|
||||||
|
}{{name: "cancellation"}, {name: "cancellation and rendering failure", renderErr: renderErr}} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
acquired, secondWrites := 0, 0
|
||||||
|
scope := func(name string, writer EffectiveWriter) Scope[struct{}, struct{}] {
|
||||||
|
return Scope[struct{}, struct{}]{Name: name, Operations: []Operation{Show}, Acquire: func(context.Context, Request, struct{}, struct{}) (commandkit.Acquired[View], error) {
|
||||||
|
acquired++
|
||||||
|
return commandkit.Acquired[View]{Value: View{Effective: writer}}, nil
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
app := newConfigTestApp(t, Command(Suite[struct{}, struct{}]{Scopes: []Scope[struct{}, struct{}]{
|
||||||
|
scope("app", effectiveWriterFunc(func(w io.Writer) error { _, _ = io.WriteString(w, "name: app\n"); cancel(); return tc.renderErr })),
|
||||||
|
scope("promptkit", effectiveWriterFunc(func(w io.Writer) error { secondWrites++; _, err := io.WriteString(w, "name: promptkit\n"); return err })),
|
||||||
|
}}))
|
||||||
|
var output bytes.Buffer
|
||||||
|
got := app.Execute(ctx, []string{"config", "show", "--scope=all"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
||||||
|
if acquired != 2 || secondWrites != 0 || output.Len() != 0 || !errors.Is(got.Cause, context.Canceled) || got.Presentation.Output.WriteError != nil {
|
||||||
|
t.Fatalf("acquired=%d second=%d output=%q outcome=%#v", acquired, secondWrites, output.String(), got)
|
||||||
|
}
|
||||||
|
if tc.renderErr == nil && got.Presentation.Output.RenderError != nil || tc.renderErr != nil && !errors.Is(got.Presentation.Output.RenderError, tc.renderErr) {
|
||||||
|
t.Fatalf("render evidence = %#v", got.Presentation.Output)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -66,11 +66,13 @@ func runConfigShow[I, S any](invocation commandkit.Invocation, plan *configOpera
|
|||||||
return configFailure("Configuration show was canceled.", commandkit.Explanation{}, err)
|
return configFailure("Configuration show was canceled.", commandkit.Explanation{}, err)
|
||||||
}
|
}
|
||||||
var value any
|
var value any
|
||||||
|
var canceled error
|
||||||
err := reporter.RenderOutput(func(io.Writer) error {
|
err := reporter.RenderOutput(func(io.Writer) error {
|
||||||
values := make(map[string]any, len(documents))
|
values := make(map[string]any, len(documents))
|
||||||
for _, document := range documents {
|
for _, document := range documents {
|
||||||
if err := invocation.Context.Err(); err != nil {
|
if err := invocation.Context.Err(); err != nil {
|
||||||
return err
|
canceled = err
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
var native bytes.Buffer
|
var native bytes.Buffer
|
||||||
if err := document.view.Effective.WriteEffective(&native); err != nil {
|
if err := document.view.Effective.WriteEffective(&native); err != nil {
|
||||||
@@ -89,6 +91,9 @@ func runConfigShow[I, S any](invocation commandkit.Invocation, plan *configOpera
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if canceled != nil {
|
||||||
|
return configFailure("Configuration show was canceled.", commandkit.Explanation{}, canceled)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if canceled := invocation.Context.Err(); canceled != nil {
|
if canceled := invocation.Context.Err(); canceled != nil {
|
||||||
return configFailure("Configuration show was canceled.", commandkit.Explanation{}, canceled)
|
return configFailure("Configuration show was canceled.", commandkit.Explanation{}, canceled)
|
||||||
|
|||||||
Reference in New Issue
Block a user