Validate normalize retry diagnostics

This commit is contained in:
2026-07-26 02:43:28 +00:00
parent d3c4d6f133
commit 80ec939383
5 changed files with 154 additions and 10 deletions

View File

@@ -150,14 +150,112 @@ func TestRunnerHandlesRetryableNormalizeFallbacks(t *testing.T) {
}
}
func TestRunnerRejectsBlankNormalizeRetryDiagnostic(t *testing.T) {
prepared := preparedAttemptDebugPipeline(t)
prepared.Steps[0].lanes[0].typed.normalize = func(context.Context, any, contracts.TypedNormalizeRequest[any]) (erasedTypedResult, error) {
return erasedTypedResult{Value: codecNotes{Items: []string{"safe"}}, Retry: &contracts.NormalizeRetry{ReasonCode: " ", Message: "missing reason"}}, nil
func TestRunnerValidatesNormalizeRetryDiagnostics(t *testing.T) {
const (
reasonSentinel = "reason-diagnostic-sentinel"
messageSentinel = "message-diagnostic-sentinel"
)
reasonOverLimit := strings.Repeat("r", contracts.MaxNormalizeRetryReasonCodeBytes-len(reasonSentinel)) + reasonSentinel + "x"
messageOverLimit := strings.Repeat("m", contracts.MaxNormalizeRetryMessageBytes-len(messageSentinel)) + messageSentinel + "x"
tests := []struct {
name string
retry contracts.NormalizeRetry
wantError string
hiddenValues []string
}{
{
name: "accepts byte limits",
retry: contracts.NormalizeRetry{
ReasonCode: strings.Repeat("r", contracts.MaxNormalizeRetryReasonCodeBytes),
Message: strings.Repeat("m", contracts.MaxNormalizeRetryMessageBytes),
},
},
{
name: "rejects oversized reason code",
retry: contracts.NormalizeRetry{
ReasonCode: reasonOverLimit,
Message: messageSentinel,
},
wantError: "reason code exceeds maximum length",
hiddenValues: []string{reasonSentinel, messageSentinel},
},
{
name: "rejects oversized message",
retry: contracts.NormalizeRetry{
ReasonCode: reasonSentinel,
Message: messageOverLimit,
},
wantError: "message exceeds maximum length",
hiddenValues: []string{reasonSentinel, messageSentinel},
},
{
name: "rejects invalid reason code UTF-8",
retry: contracts.NormalizeRetry{
ReasonCode: reasonSentinel + string([]byte{0xff}),
Message: messageSentinel,
},
wantError: "reason code has invalid UTF-8",
hiddenValues: []string{reasonSentinel, messageSentinel},
},
{
name: "rejects invalid message UTF-8",
retry: contracts.NormalizeRetry{
ReasonCode: reasonSentinel,
Message: messageSentinel + string([]byte{0xff}),
},
wantError: "message has invalid UTF-8",
hiddenValues: []string{reasonSentinel, messageSentinel},
},
{
name: "rejects blank reason code",
retry: contracts.NormalizeRetry{
ReasonCode: " \t\n ",
Message: messageSentinel,
},
wantError: "reason code is blank",
hiddenValues: []string{messageSentinel},
},
{
name: "rejects blank message",
retry: contracts.NormalizeRetry{
ReasonCode: reasonSentinel,
Message: " \t\n ",
},
wantError: "message is blank",
hiddenValues: []string{reasonSentinel},
},
}
_, err := New().Run(context.Background(), RunInput{Prepared: prepared, RawInput: []byte("input"), Debug: newCapturedDebugRecorder()})
if err == nil || !strings.Contains(err.Error(), "blank reason code or message") {
t.Fatalf("Run() error = %v, want retry diagnostic contract error", err)
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
prepared := preparedAttemptDebugPipeline(t)
prepared.Steps[0].lanes[0].typed.normalize = func(context.Context, any, contracts.TypedNormalizeRequest[any]) (erasedTypedResult, error) {
return erasedTypedResult{Value: codecNotes{Items: []string{"safe"}}, Retry: &tc.retry}, nil
}
debug := newCapturedDebugRecorder()
output, err := New().Run(context.Background(), RunInput{Prepared: prepared, RawInput: []byte("input"), Debug: debug})
if tc.wantError == "" {
if err != nil {
t.Fatalf("Run() error = %v, want accepted retry fallback", err)
}
if len(output.NormalizeOutputs) != 1 {
t.Fatalf("normalize outputs = %#v, want one accepted fallback", output.NormalizeOutputs)
}
return
}
if err == nil || !strings.Contains(err.Error(), tc.wantError) {
t.Fatalf("Run() error = %v, want fixed %q error", err, tc.wantError)
}
var debugOutput strings.Builder
for _, name := range debug.names() {
debugOutput.Write(debug.json[name])
}
for _, value := range tc.hiddenValues {
if strings.Contains(err.Error(), value) || strings.Contains(debugOutput.String(), value) {
t.Fatalf("retry diagnostic leaked %q", value)
}
}
})
}
}