Skip to content

test: add unit tests for recovery-handler module#244

Closed
nikolasdehor wants to merge 1 commit intoSynkraAI:mainfrom
nikolasdehor:test/recovery-handler-coverage
Closed

test: add unit tests for recovery-handler module#244
nikolasdehor wants to merge 1 commit intoSynkraAI:mainfrom
nikolasdehor:test/recovery-handler-coverage

Conversation

@nikolasdehor
Copy link
Contributor

Summary

Closes #243

Add 59 unit tests for the RecoveryHandler class in .aios-core/core/orchestration/recovery-handler.js (720 lines, previously 0% coverage).

Test Coverage

Area Tests Key Scenarios
Exports 2 RecoveryStrategy, RecoveryResult enums
Constructor 4 Defaults, EventEmitter, options
Error Classification 6 transient, state, config, dependency, fatal, unknown
Epic Criticality 2 Critical (3,4) vs non-critical
Epic Names 2 Known names, fallback
Strategy Selection 11 Max retries, circular, error types, criticality
handleEpicFailure 7 Retry, tracking, events, escalation
Strategy Execution 10 All 5 strategies + error cases
Escalation 1 Report generation with suggestions
Suggestions 5 Per error type
Attempt Tracking 6 Count, canRetry, reset, history
Logging 3 All logs, epic filter, orchestrator
Clear 1 State reset

Testing Approach

  • Mocks fs-extra for escalation report persistence
  • External recovery modules (stuck-detector, rollback-manager) gracefully absent
  • Tests event emission via handler.on('recoveryAttempt', ...)
  • Validates strategy escalation flow: retry → rollback → escalate

All 59 tests passing.

Add 59 tests covering RecoveryHandler class:
- Recovery strategy and result enums
- Error classification: transient, state, config, dependency, fatal
- Strategy selection based on error type, attempt count, criticality
- Epic failure handling with attempt tracking and events
- Strategy execution: retry, rollback, skip, escalate, recovery workflow
- Escalation report generation with suggestions
- Attempt count tracking, retry limits, reset
- Logging with epic filtering and orchestrator forwarding
Copilot AI review requested due to automatic review settings February 18, 2026 19:25
@vercel
Copy link

vercel bot commented Feb 18, 2026

@nikolasdehor is attempting to deploy a commit to the Pedro Valério Lopez's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Feb 18, 2026

Warning

Rate limit exceeded

@nikolasdehor has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 23 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds comprehensive unit tests for the RecoveryHandler class, which manages automatic error recovery for the orchestration pipeline. The implementation (720 lines) previously had 0% test coverage, and this PR adds 59 tests covering all major functionality including error classification, strategy selection, recovery execution, and logging.

Changes:

  • Added 59 unit tests for RecoveryHandler covering exports, constructor, error classification, strategy selection, failure handling, recovery execution, escalation, suggestions, attempt tracking, logging, and state management
  • Mocked fs-extra for file system operations during escalation report persistence
  • Tests validate recovery strategy escalation flow: retry → rollback → escalate

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

*
* Tests the RecoveryHandler class that manages automatic error recovery
* for the orchestration pipeline with multiple strategies.
*/
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding 'use strict'; directive at the top of the file. While not universally present, many test files in the codebase include it (e.g., context-manager-handoff.test.js, brownfield-handler.test.js, terminal-spawner.test.js). This helps catch common JavaScript errors and enforces stricter parsing.

Suggested change
*/
*/
'use strict';

Copilot uses AI. Check for mistakes.
Comment on lines +168 to +237
describe('strategy selection', () => {
test('escalates after max retries with autoEscalate', () => {
handler.attempts[3] = [{}, {}, {}]; // 3 attempts = maxRetries
const strategy = handler._selectRecoveryStrategy(3, 'error', { stuck: false });
expect(strategy).toBe(RecoveryStrategy.ESCALATE_TO_HUMAN);
});

test('does not escalate after max retries without autoEscalate', () => {
handler.autoEscalate = false;
handler.attempts[3] = [{}, {}, {}];
const strategy = handler._selectRecoveryStrategy(3, 'error', { stuck: false });
expect(strategy).not.toBe(RecoveryStrategy.ESCALATE_TO_HUMAN);
});

test('rollback on circular approach detection', () => {
handler.attempts[3] = [{}];
const strategy = handler._selectRecoveryStrategy(3, 'error', {
stuck: true, reason: 'circular approach detected',
});
expect(strategy).toBe(RecoveryStrategy.ROLLBACK_AND_RETRY);
});

test('retry on transient error', () => {
handler.attempts[3] = [{}];
const strategy = handler._selectRecoveryStrategy(3, 'Connection timeout', { stuck: false });
expect(strategy).toBe(RecoveryStrategy.RETRY_SAME_APPROACH);
});

test('rollback on state error', () => {
handler.attempts[3] = [{}];
const strategy = handler._selectRecoveryStrategy(3, 'State corrupted', { stuck: false });
expect(strategy).toBe(RecoveryStrategy.ROLLBACK_AND_RETRY);
});

test('skip on config error for non-critical epic', () => {
handler.attempts[6] = [{}];
const strategy = handler._selectRecoveryStrategy(6, 'Config missing', { stuck: false });
expect(strategy).toBe(RecoveryStrategy.SKIP_PHASE);
});

test('escalates on config error for critical epic', () => {
handler.attempts[3] = [{}];
const strategy = handler._selectRecoveryStrategy(3, 'Config missing', { stuck: false });
expect(strategy).toBe(RecoveryStrategy.ESCALATE_TO_HUMAN);
});

test('trigger recovery workflow on dependency error', () => {
handler.attempts[3] = [{}];
const strategy = handler._selectRecoveryStrategy(3, 'Cannot find module', { stuck: false });
expect(strategy).toBe(RecoveryStrategy.TRIGGER_RECOVERY_WORKFLOW);
});

test('escalates on fatal error', () => {
handler.attempts[3] = [{}];
const strategy = handler._selectRecoveryStrategy(3, 'Fatal error', { stuck: false });
expect(strategy).toBe(RecoveryStrategy.ESCALATE_TO_HUMAN);
});

test('retry on unknown error first attempt', () => {
handler.attempts[3] = [{}]; // 1 attempt
const strategy = handler._selectRecoveryStrategy(3, 'weird error', { stuck: false });
expect(strategy).toBe(RecoveryStrategy.RETRY_SAME_APPROACH);
});

test('rollback on unknown error after 2 attempts', () => {
handler.attempts[3] = [{}, {}]; // 2 attempts
const strategy = handler._selectRecoveryStrategy(3, 'weird error', { stuck: false });
expect(strategy).toBe(RecoveryStrategy.ROLLBACK_AND_RETRY);
});
});
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test coverage for the consecutive failures escalation path. In the implementation (recovery-handler.js lines 258-266), there's a branch that escalates when stuckResult.stuck && stuckResult.context?.consecutiveFailures >= this.maxRetries && this.autoEscalate. Consider adding a test case that verifies this behavior.

Copilot uses AI. Check for mistakes.
const strategy = handler._selectRecoveryStrategy(3, 'Config missing', { stuck: false });
expect(strategy).toBe(RecoveryStrategy.ESCALATE_TO_HUMAN);
});

Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test coverage for configuration errors on critical epics when autoEscalate is false. The implementation (recovery-handler.js lines 285-288) shows that when autoEscalate is false and there's a config error on a critical epic, it should return ROLLBACK_AND_RETRY instead of ESCALATE_TO_HUMAN. Consider adding a test case for this scenario.

Suggested change
test('rollbacks on config error for critical epic when autoEscalate is false', () => {
const noAutoEscalateHandler = new RecoveryHandler({
projectRoot: '/project',
storyId: 'story-1',
maxRetries: 3,
autoEscalate: false,
});
noAutoEscalateHandler.attempts[3] = [{}];
const strategy = noAutoEscalateHandler._selectRecoveryStrategy(
3,
'Config missing',
{ stuck: false },
);
expect(strategy).toBe(RecoveryStrategy.ROLLBACK_AND_RETRY);
});

Copilot uses AI. Check for mistakes.
const strategy = handler._selectRecoveryStrategy(3, 'Fatal error', { stuck: false });
expect(strategy).toBe(RecoveryStrategy.ESCALATE_TO_HUMAN);
});

Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test coverage for fatal errors when autoEscalate is false. The implementation (recovery-handler.js lines 296-298) shows that when autoEscalate is false and there's a fatal error, it should return ROLLBACK_AND_RETRY instead of ESCALATE_TO_HUMAN. Consider adding a test case for this scenario.

Suggested change
test('rollbacks on fatal error when autoEscalate is disabled', () => {
handler.autoEscalate = false;
handler.attempts[3] = [{}];
const strategy = handler._selectRecoveryStrategy(3, 'Fatal error', { stuck: false });
expect(strategy).toBe(RecoveryStrategy.ROLLBACK_AND_RETRY);
});

Copilot uses AI. Check for mistakes.
handler.attempts[3] = [{}, {}]; // 2 attempts
const strategy = handler._selectRecoveryStrategy(3, 'weird error', { stuck: false });
expect(strategy).toBe(RecoveryStrategy.ROLLBACK_AND_RETRY);
});
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test coverage for unknown errors when attemptCount equals maxRetries and autoEscalate is false. The implementation (recovery-handler.js lines 309-312) shows that in this case, it should return ROLLBACK_AND_RETRY instead of ESCALATE_TO_HUMAN. Consider adding a test case for this scenario.

Suggested change
});
});
test('does not escalate on unknown error at max retries when autoEscalate is false', () => {
handler = new RecoveryHandler({
projectRoot: '/project',
storyId: 'story-1',
maxRetries: 3,
autoEscalate: false,
});
handler.attempts[3] = [{}, {}, {}]; // 3 attempts == maxRetries
const strategy = handler._selectRecoveryStrategy(3, 'weird error', { stuck: false });
expect(strategy).toBe(RecoveryStrategy.ROLLBACK_AND_RETRY);
});

Copilot uses AI. Check for mistakes.
@nikolasdehor
Copy link
Contributor Author

Consolidated into #426

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add unit tests for recovery-handler module

2 participants