Problem
RetryableTask.computeNextDelayInMilliseconds() has three untested code paths that guard against critical failure modes:
-
Non-retriable failure guard (line 63-65): Returns undefined when lastFailure.isNonRetriable is true, preventing retries of fundamentally broken operations (e.g., activity not found, version mismatch). No test covers this path.
-
Delay-exceeds-remaining-timeout guard (lines 106-113): Returns undefined when the computed retry delay would exceed the remaining timeout window. The existing timeout test only verifies that elapsed time exceeding the timeout stops retries, but not that a computed delay overshooting the remaining time stops retries. These are two distinct code paths in the method (lines 86-91 vs lines 106-113).
-
complete() override from RetryTaskBase (line 129-136): Clears _exception and _lastFailure when a retry succeeds. While RetryHandlerTask tests this behavior, RetryableTask (the more commonly used policy-based retry) does not, despite being a separate subclass.
File references:
packages/durabletask-js/src/task/retryable-task.ts lines 61-116
packages/durabletask-js/src/task/retry-task-base.ts lines 129-136
packages/durabletask-js/test/retryable-task.spec.ts (existing tests)
Root Cause
The existing test suite covers standard retry scenarios (backoff, max attempts, elapsed timeout, handleFailure predicate) but omits these edge-case guards that protect against:
- Infinite retry loops for non-retriable failures
- Scheduling timers past the timeout window
- Stale failure state leaking across retry success
Proposed Fix
Add three new test cases to retryable-task.spec.ts covering each untested path.
Impact
Severity: Medium — Without test coverage, a regression removing the non-retriable guard would cause infinite retry loops for "activity not found" errors. The remaining-timeout guard prevents scheduling pointless timers.
Confidence: High — These are concrete, verifiable code paths with zero test coverage.
Problem
RetryableTask.computeNextDelayInMilliseconds()has three untested code paths that guard against critical failure modes:Non-retriable failure guard (line 63-65): Returns
undefinedwhenlastFailure.isNonRetriableistrue, preventing retries of fundamentally broken operations (e.g., activity not found, version mismatch). No test covers this path.Delay-exceeds-remaining-timeout guard (lines 106-113): Returns
undefinedwhen the computed retry delay would exceed the remaining timeout window. The existing timeout test only verifies that elapsed time exceeding the timeout stops retries, but not that a computed delay overshooting the remaining time stops retries. These are two distinct code paths in the method (lines 86-91 vs lines 106-113).complete()override fromRetryTaskBase(line 129-136): Clears_exceptionand_lastFailurewhen a retry succeeds. WhileRetryHandlerTasktests this behavior,RetryableTask(the more commonly used policy-based retry) does not, despite being a separate subclass.File references:
packages/durabletask-js/src/task/retryable-task.tslines 61-116packages/durabletask-js/src/task/retry-task-base.tslines 129-136packages/durabletask-js/test/retryable-task.spec.ts(existing tests)Root Cause
The existing test suite covers standard retry scenarios (backoff, max attempts, elapsed timeout, handleFailure predicate) but omits these edge-case guards that protect against:
Proposed Fix
Add three new test cases to
retryable-task.spec.tscovering each untested path.Impact
Severity: Medium — Without test coverage, a regression removing the non-retriable guard would cause infinite retry loops for "activity not found" errors. The remaining-timeout guard prevents scheduling pointless timers.
Confidence: High — These are concrete, verifiable code paths with zero test coverage.