(t *testing.T)
| 487 | } |
| 488 | |
| 489 | func TestIsRetryableError(t *testing.T) { |
| 490 | t.Parallel() |
| 491 | |
| 492 | tests := []struct { |
| 493 | name string |
| 494 | err error |
| 495 | retryable bool |
| 496 | }{ |
| 497 | {"Nil", nil, false}, |
| 498 | {"ContextCanceled", context.Canceled, false}, |
| 499 | {"ContextDeadlineExceeded", context.DeadlineExceeded, false}, |
| 500 | {"WrappedContextCanceled", xerrors.Errorf("wrapped: %w", context.Canceled), false}, |
| 501 | {"DNSError", &net.DNSError{Err: "no such host", Name: "example.com", IsNotFound: true}, true}, |
| 502 | {"OpError", &net.OpError{Op: "dial", Net: "tcp", Err: &os.SyscallError{}}, true}, |
| 503 | {"WrappedDNSError", xerrors.Errorf("connect: %w", &net.DNSError{Err: "no such host", Name: "example.com"}), true}, |
| 504 | {"SDKError_500", codersdk.NewTestError(http.StatusInternalServerError, "GET", "/api"), true}, |
| 505 | {"SDKError_502", codersdk.NewTestError(http.StatusBadGateway, "GET", "/api"), true}, |
| 506 | {"SDKError_503", codersdk.NewTestError(http.StatusServiceUnavailable, "GET", "/api"), true}, |
| 507 | {"SDKError_401", codersdk.NewTestError(http.StatusUnauthorized, "GET", "/api"), false}, |
| 508 | {"SDKError_403", codersdk.NewTestError(http.StatusForbidden, "GET", "/api"), false}, |
| 509 | {"SDKError_404", codersdk.NewTestError(http.StatusNotFound, "GET", "/api"), false}, |
| 510 | {"GenericError", xerrors.New("something went wrong"), false}, |
| 511 | } |
| 512 | |
| 513 | for _, tt := range tests { |
| 514 | t.Run(tt.name, func(t *testing.T) { |
| 515 | t.Parallel() |
| 516 | assert.Equal(t, tt.retryable, isRetryableError(tt.err)) |
| 517 | }) |
| 518 | } |
| 519 | |
| 520 | // net.Dialer.Timeout produces *net.OpError that matches both |
| 521 | // IsConnectionError and context.DeadlineExceeded. Verify it is retryable. |
| 522 | t.Run("DialTimeout", func(t *testing.T) { |
| 523 | t.Parallel() |
| 524 | ctx, cancel := context.WithDeadline(context.Background(), time.Now()) |
| 525 | defer cancel() |
| 526 | <-ctx.Done() // ensure deadline has fired |
| 527 | _, err := (&net.Dialer{}).DialContext(ctx, "tcp", "127.0.0.1:1") |
| 528 | require.Error(t, err) |
| 529 | // Proves the ambiguity: this error matches BOTH checks. |
| 530 | require.ErrorIs(t, err, context.DeadlineExceeded) |
| 531 | require.ErrorAs(t, err, new(*net.OpError)) |
| 532 | assert.True(t, isRetryableError(err)) |
| 533 | // Also when wrapped, as runCoderConnectStdio does. |
| 534 | assert.True(t, isRetryableError(xerrors.Errorf("dial coder connect: %w", err))) |
| 535 | }) |
| 536 | } |
| 537 | |
| 538 | func TestRetryWithInterval(t *testing.T) { |
| 539 | t.Parallel() |
nothing calls this directly
no test coverage detected