Preserve empty smoke error messages in aggregate reports
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Dependency upgrade and PromptKit smoke review remediation
|
||||
|
||||
Status: open; R-1 through R-4 require attention.
|
||||
Status: in progress; R-1 complete; R-2 through R-4 remain open.
|
||||
|
||||
## Review scope and evidence
|
||||
|
||||
@@ -34,13 +34,14 @@ modules. No test suite was run or added during this review.
|
||||
|
||||
The completed implementation plan records earlier test runs; those completion
|
||||
markers are historical evidence, not fresh test results from this review.
|
||||
No production correction has been applied here. The preceding completed
|
||||
No production correction was applied during the review. Completion records
|
||||
below describe the subsequent remediation. The preceding completed
|
||||
request-validation review remains available in
|
||||
[Git history](https://gitea.maximumdirect.net/eric/CommandKit/src/commit/730f930b47c929fb2c59a25a75cbe2924dfc75bb/docs/roadmap/remediation.md).
|
||||
|
||||
## R-1: Preserve aggregate error presence when its message is empty
|
||||
|
||||
Status: open. Priority: P2. Category: report fidelity.
|
||||
Status: complete. Priority: P2. Category: report fidelity.
|
||||
|
||||
**Location:** [smoke_report.go](../../integrations/ckprompt/smoke_report.go),
|
||||
`smokeResultDTO` and `encodeSmokeResults`, lines 101–125.
|
||||
@@ -69,6 +70,14 @@ message remains `error: ""`. Confirm the assembled aggregate still returns
|
||||
operational failure and preserves the original error through `errors.Is`.
|
||||
The appropriate focused tests and ckprompt suite pass.
|
||||
|
||||
**Completed:** 2026-10-18. Changed the private aggregate error field to an
|
||||
optional string pointer, preserving non-nil empty messages. Extended the YAML
|
||||
projection and assembled aggregate regressions to distinguish nil, empty, and
|
||||
nonempty errors while retaining operational classification and typed cause
|
||||
identity. Both focused regressions failed before the fix and passed afterward.
|
||||
The complete ckprompt suite, including the composed consumer, passed with
|
||||
Go 1.26.8 and offline dependency resolution.
|
||||
|
||||
## R-2: Remove assertions about PromptKit's private smoke-test execution
|
||||
|
||||
Status: open. Priority: P3. Category: upstream testing ownership.
|
||||
|
||||
@@ -141,7 +141,7 @@ func (err *smokeTypedError) Error() string { return err.text }
|
||||
|
||||
func TestSmokeAggregatePreservesMultipleTypedCauses(t *testing.T) {
|
||||
firstErr := &smokeTypedError{text: "reachability failed"}
|
||||
secondErr := &smokeTypedError{text: "authentication failed"}
|
||||
secondErr := &smokeTypedError{text: ""}
|
||||
app := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{
|
||||
SmokeTest: func(_ context.Context, request SmokeRequest, _ struct{}, _ struct{}) (commandkit.Acquired[*promptkit.SmokeTestReport], error) {
|
||||
var err error
|
||||
@@ -162,14 +162,14 @@ func TestSmokeAggregatePreservesMultipleTypedCauses(t *testing.T) {
|
||||
}
|
||||
var document struct {
|
||||
Tests []struct {
|
||||
Kind string `yaml:"kind"`
|
||||
Error string `yaml:"error"`
|
||||
Kind string `yaml:"kind"`
|
||||
Error *string `yaml:"error"`
|
||||
} `yaml:"tests"`
|
||||
}
|
||||
if err := yaml.Unmarshal(output.Bytes(), &document); err != nil {
|
||||
t.Fatalf("decode aggregate errors %q: %v", output.String(), err)
|
||||
}
|
||||
if len(document.Tests) != 4 || document.Tests[0].Kind != string(promptkit.SmokeTestReachability) || document.Tests[0].Error != firstErr.Error() || document.Tests[1].Kind != string(promptkit.SmokeTestAuthentication) || document.Tests[1].Error != secondErr.Error() || document.Tests[2].Error != "" || document.Tests[3].Error != "" {
|
||||
if len(document.Tests) != 4 || document.Tests[0].Kind != string(promptkit.SmokeTestReachability) || document.Tests[0].Error == nil || *document.Tests[0].Error != firstErr.Error() || document.Tests[1].Kind != string(promptkit.SmokeTestAuthentication) || document.Tests[1].Error == nil || *document.Tests[1].Error != secondErr.Error() || document.Tests[2].Error != nil || document.Tests[3].Error != nil {
|
||||
t.Fatalf("aggregate error entries = %#v", document.Tests)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ type smokeAggregateDTO struct {
|
||||
type smokeResultDTO struct {
|
||||
Kind promptkit.SmokeTestKind `yaml:"kind"`
|
||||
Report *smokeReportDTO `yaml:"report"`
|
||||
Error string `yaml:"error,omitempty"`
|
||||
Error *string `yaml:"error,omitempty"`
|
||||
}
|
||||
|
||||
type smokeResult struct {
|
||||
@@ -122,7 +122,8 @@ func encodeSmokeResults(results []smokeResult) ([]byte, error) {
|
||||
Report: smokeReportFromNative(result.Report),
|
||||
}
|
||||
if result.Err != nil {
|
||||
entries[index].Error = result.Err.Error()
|
||||
message := result.Err.Error()
|
||||
entries[index].Error = &message
|
||||
}
|
||||
}
|
||||
return encodePromptYAMLBytes(smokeAggregateDTO{Tests: entries})
|
||||
|
||||
@@ -163,6 +163,7 @@ func TestSmokeAggregateYAMLRetainsOrderErrorAndNativeReportKind(t *testing.T) {
|
||||
encoded, err := encodeSmokeResults([]smokeResult{
|
||||
{Kind: promptkit.SmokeTestTextInference, Report: report, Err: operationErr},
|
||||
{Kind: promptkit.SmokeTestAuthentication},
|
||||
{Kind: promptkit.SmokeTestStructuredOutput, Report: report, Err: errors.New("")},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -172,7 +173,7 @@ func TestSmokeAggregateYAMLRetainsOrderErrorAndNativeReportKind(t *testing.T) {
|
||||
t.Fatalf("decode aggregate report: %v\n%s", err, encoded)
|
||||
}
|
||||
tests, ok := document["tests"].([]any)
|
||||
if !ok || len(tests) != 2 {
|
||||
if !ok || len(tests) != 3 {
|
||||
t.Fatalf("aggregate entries = %#v", document)
|
||||
}
|
||||
first := tests[0].(map[string]any)
|
||||
@@ -190,6 +191,10 @@ func TestSmokeAggregateYAMLRetainsOrderErrorAndNativeReportKind(t *testing.T) {
|
||||
if _, exists := second["error"]; exists {
|
||||
t.Fatalf("nil error should be omitted: %#v", second)
|
||||
}
|
||||
third := tests[2].(map[string]any)
|
||||
if message, exists := third["error"]; !exists || message != "" {
|
||||
t.Fatalf("empty error message should remain present: %#v", third)
|
||||
}
|
||||
}
|
||||
|
||||
func containsSmokeKey(value any, want string) bool {
|
||||
|
||||
Reference in New Issue
Block a user