Skip to content

Commit ee67105

Browse files
danmoseleyCopilot
andcommitted
errors: extract requireErrorText and assertContextHasError test helpers
Reduces repetition in TestNewGitHubAPIErrorResponse_RateLimits subtests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d480ff7 commit ee67105

1 file changed

Lines changed: 50 additions & 60 deletions

File tree

pkg/errors/error_test.go

Lines changed: 50 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,28 @@ func TestMiddlewareScenario(t *testing.T) {
463463
})
464464
}
465465

466+
// requireErrorText asserts that result is a non-nil MCP tool error and returns its text content.
467+
func requireErrorText(t *testing.T, result *mcp.CallToolResult) string {
468+
t.Helper()
469+
require.NotNil(t, result)
470+
require.True(t, result.IsError)
471+
require.NotEmpty(t, result.Content)
472+
text, ok := result.Content[0].(*mcp.TextContent)
473+
require.True(t, ok, "expected *mcp.TextContent, got %T", result.Content[0])
474+
return text.Text
475+
}
476+
477+
// assertContextHasError asserts that exactly one error is stored in ctx and it matches expectedErr.
478+
//
479+
//nolint:revive // t must be first for test helpers; context-as-argument doesn't apply here
480+
func assertContextHasError(t *testing.T, ctx context.Context, expectedErr error) {
481+
t.Helper()
482+
apiErrors, err := GetGitHubAPIErrors(ctx)
483+
require.NoError(t, err)
484+
require.Len(t, apiErrors, 1)
485+
assert.Equal(t, expectedErr, apiErrors[0].Err)
486+
}
487+
466488
func TestNewGitHubAPIErrorResponse_RateLimits(t *testing.T) {
467489
t.Run("RateLimitError produces clean message with retry time", func(t *testing.T) {
468490
// Given a context with GitHub error tracking enabled
@@ -482,21 +504,14 @@ func TestNewGitHubAPIErrorResponse_RateLimits(t *testing.T) {
482504
// When we create an API error response for a rate limit error
483505
result := NewGitHubAPIErrorResponse(ctx, "search code", resp, rateLimitErr)
484506

485-
// Then it should return an MCP error result
486-
require.NotNil(t, result)
487-
assert.True(t, result.IsError)
488-
489-
// And the message should be clean and actionable (no raw URLs or status codes)
490-
textContent := result.Content[0].(*mcp.TextContent)
491-
assert.Contains(t, textContent.Text, fmt.Sprintf("GitHub API rate limit exceeded. Retry after %v.", expectedRetryIn))
492-
assert.NotContains(t, textContent.Text, "https://")
493-
assert.NotContains(t, textContent.Text, "403")
507+
// Then the message should be clean and actionable (no raw URLs or status codes)
508+
text := requireErrorText(t, result)
509+
assert.Contains(t, text, fmt.Sprintf("GitHub API rate limit exceeded. Retry after %v.", expectedRetryIn))
510+
assert.NotContains(t, text, "https://")
511+
assert.NotContains(t, text, "403")
494512

495513
// And the original error should still be stored in context for middleware
496-
apiErrors, err := GetGitHubAPIErrors(ctx)
497-
require.NoError(t, err)
498-
require.Len(t, apiErrors, 1)
499-
assert.Equal(t, rateLimitErr, apiErrors[0].Err)
514+
assertContextHasError(t, ctx, rateLimitErr)
500515
})
501516

502517
t.Run("AbuseRateLimitError with RetryAfter produces clean message with wait time", func(t *testing.T) {
@@ -514,21 +529,14 @@ func TestNewGitHubAPIErrorResponse_RateLimits(t *testing.T) {
514529
// When we create an API error response for a secondary rate limit error
515530
result := NewGitHubAPIErrorResponse(ctx, "create issue", resp, abuseErr)
516531

517-
// Then it should return an MCP error result
518-
require.NotNil(t, result)
519-
assert.True(t, result.IsError)
520-
521532
// And the message should include the specific retry duration
522-
textContent := result.Content[0].(*mcp.TextContent)
523-
assert.Contains(t, textContent.Text, "GitHub secondary rate limit exceeded. Retry after 47s.")
524-
assert.NotContains(t, textContent.Text, "https://")
525-
assert.NotContains(t, textContent.Text, "403")
533+
text := requireErrorText(t, result)
534+
assert.Contains(t, text, "GitHub secondary rate limit exceeded. Retry after 47s.")
535+
assert.NotContains(t, text, "https://")
536+
assert.NotContains(t, text, "403")
526537

527538
// And the original error should still be stored in context for middleware
528-
apiErrors, err := GetGitHubAPIErrors(ctx)
529-
require.NoError(t, err)
530-
require.Len(t, apiErrors, 1)
531-
assert.Equal(t, abuseErr, apiErrors[0].Err)
539+
assertContextHasError(t, ctx, abuseErr)
532540
})
533541

534542
t.Run("AbuseRateLimitError without RetryAfter produces clean message without wait time", func(t *testing.T) {
@@ -545,21 +553,14 @@ func TestNewGitHubAPIErrorResponse_RateLimits(t *testing.T) {
545553
// When we create an API error response for a secondary rate limit error without retry info
546554
result := NewGitHubAPIErrorResponse(ctx, "create issue", resp, abuseErr)
547555

548-
// Then it should return an MCP error result
549-
require.NotNil(t, result)
550-
assert.True(t, result.IsError)
551-
552556
// And the message should be clean and actionable
553-
textContent := result.Content[0].(*mcp.TextContent)
554-
assert.Contains(t, textContent.Text, "GitHub secondary rate limit exceeded. Wait before retrying.")
555-
assert.NotContains(t, textContent.Text, "https://")
556-
assert.NotContains(t, textContent.Text, "403")
557+
text := requireErrorText(t, result)
558+
assert.Contains(t, text, "GitHub secondary rate limit exceeded. Wait before retrying.")
559+
assert.NotContains(t, text, "https://")
560+
assert.NotContains(t, text, "403")
557561

558562
// And the original error should still be stored in context for middleware
559-
apiErrors, err := GetGitHubAPIErrors(ctx)
560-
require.NoError(t, err)
561-
require.Len(t, apiErrors, 1)
562-
assert.Equal(t, abuseErr, apiErrors[0].Err)
563+
assertContextHasError(t, ctx, abuseErr)
563564
})
564565

565566
t.Run("RateLimitError with reset time in the past falls back to wait message", func(t *testing.T) {
@@ -575,10 +576,8 @@ func TestNewGitHubAPIErrorResponse_RateLimits(t *testing.T) {
575576

576577
result := NewGitHubAPIErrorResponse(ctx, "search code", resp, rateLimitErr)
577578

578-
require.NotNil(t, result)
579-
assert.True(t, result.IsError)
580-
textContent := result.Content[0].(*mcp.TextContent)
581-
assert.Contains(t, textContent.Text, "GitHub API rate limit exceeded. Wait before retrying.")
579+
text := requireErrorText(t, result)
580+
assert.Contains(t, text, "GitHub API rate limit exceeded. Wait before retrying.")
582581
})
583582

584583
t.Run("RateLimitError with zero reset time falls back to wait message", func(t *testing.T) {
@@ -593,10 +592,8 @@ func TestNewGitHubAPIErrorResponse_RateLimits(t *testing.T) {
593592

594593
result := NewGitHubAPIErrorResponse(ctx, "search code", resp, rateLimitErr)
595594

596-
require.NotNil(t, result)
597-
assert.True(t, result.IsError)
598-
textContent := result.Content[0].(*mcp.TextContent)
599-
assert.Contains(t, textContent.Text, "GitHub API rate limit exceeded. Wait before retrying.")
595+
text := requireErrorText(t, result)
596+
assert.Contains(t, text, "GitHub API rate limit exceeded. Wait before retrying.")
600597
})
601598

602599
t.Run("wrapped RateLimitError is handled via errors.As", func(t *testing.T) {
@@ -616,11 +613,9 @@ func TestNewGitHubAPIErrorResponse_RateLimits(t *testing.T) {
616613

617614
result := NewGitHubAPIErrorResponse(ctx, "search code", resp, wrappedErr)
618615

619-
require.NotNil(t, result)
620-
assert.True(t, result.IsError)
621-
textContent := result.Content[0].(*mcp.TextContent)
622-
assert.Contains(t, textContent.Text, fmt.Sprintf("GitHub API rate limit exceeded. Retry after %v.", expectedRetryIn))
623-
assert.NotContains(t, textContent.Text, "https://")
616+
text := requireErrorText(t, result)
617+
assert.Contains(t, text, fmt.Sprintf("GitHub API rate limit exceeded. Retry after %v.", expectedRetryIn))
618+
assert.NotContains(t, text, "https://")
624619
})
625620

626621
t.Run("wrapped AbuseRateLimitError is handled via errors.As", func(t *testing.T) {
@@ -637,11 +632,9 @@ func TestNewGitHubAPIErrorResponse_RateLimits(t *testing.T) {
637632

638633
result := NewGitHubAPIErrorResponse(ctx, "create issue", resp, wrappedErr)
639634

640-
require.NotNil(t, result)
641-
assert.True(t, result.IsError)
642-
textContent := result.Content[0].(*mcp.TextContent)
643-
assert.Contains(t, textContent.Text, "GitHub secondary rate limit exceeded. Retry after 30s.")
644-
assert.NotContains(t, textContent.Text, "https://")
635+
text := requireErrorText(t, result)
636+
assert.Contains(t, text, "GitHub secondary rate limit exceeded. Retry after 30s.")
637+
assert.NotContains(t, text, "https://")
645638
})
646639

647640
t.Run("non-rate-limit GitHub API error passes through the original error message", func(t *testing.T) {
@@ -655,10 +648,7 @@ func TestNewGitHubAPIErrorResponse_RateLimits(t *testing.T) {
655648
result := NewGitHubAPIErrorResponse(ctx, "API call failed", resp, originalErr)
656649

657650
// Then the message should contain the original error text unchanged
658-
require.NotNil(t, result)
659-
assert.True(t, result.IsError)
660-
661-
textContent := result.Content[0].(*mcp.TextContent)
662-
assert.Contains(t, textContent.Text, "validation failed")
651+
text := requireErrorText(t, result)
652+
assert.Contains(t, text, "validation failed")
663653
})
664654
}

0 commit comments

Comments
 (0)