(t *testing.T)
| 99 | } |
| 100 | |
| 101 | func TestRetryBindingCancellation(t *testing.T) { |
| 102 | var attempts atomic.Int32 |
| 103 | op := buildZeroArgAsync(t, async.RetryBinding(func(ctx context.Context, args ...ref.Val) ref.Val { |
| 104 | attempts.Add(1) |
| 105 | return types.WrapErr(retryableTestErr{}) |
| 106 | }, async.RetryAttempts(5), async.RetryBackoff(500*time.Millisecond))) |
| 107 | |
| 108 | ctx, cancel := context.WithCancel(context.Background()) |
| 109 | go func() { |
| 110 | time.Sleep(40 * time.Millisecond) |
| 111 | cancel() |
| 112 | }() |
| 113 | |
| 114 | start := time.Now() |
| 115 | var res ref.Val |
| 116 | select { |
| 117 | case res = <-op(ctx): |
| 118 | case <-time.After(2 * time.Second): |
| 119 | t.Fatal("retry op did not return after cancellation") |
| 120 | } |
| 121 | elapsed := time.Since(start) |
| 122 | |
| 123 | if !types.IsError(res) || !strings.Contains(res.(*types.Err).Error(), "cancelled") { |
| 124 | t.Errorf("result = %v, want a cancellation error", res) |
| 125 | } |
| 126 | // Cancellation must interrupt the backoff wait rather than running it to completion. |
| 127 | if elapsed >= 500*time.Millisecond { |
| 128 | t.Errorf("retry waited the full backoff (%v); cancellation did not interrupt it", elapsed) |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | func TestRetryNonRetryableError(t *testing.T) { |
| 133 | var attempts atomic.Int32 |
nothing calls this directly
no test coverage detected