Skip to content

Created centralized unwrapping messenger exceptions#25

Merged
stixx merged 2 commits into
mainfrom
centralize-wrapped-exception-unwrap
May 9, 2026
Merged

Created centralized unwrapping messenger exceptions#25
stixx merged 2 commits into
mainfrom
centralize-wrapped-exception-unwrap

Conversation

@stixx
Copy link
Copy Markdown
Owner

@stixx stixx commented May 9, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Exception handling in command dispatching and API error responses now properly unwraps nested exceptions before processing.
  • Tests

    • Added comprehensive test coverage for exception unwrapping across multiple scenarios.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

Warning

Rate limit exceeded

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

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ 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.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 83328f0c-689a-429a-8ba4-a3c8044a43cb

📥 Commits

Reviewing files that changed from the base of the PR and between 1621540 and b7fdcd2.

📒 Files selected for processing (1)
  • tests/Unit/Exception/WrappedExceptionUnwrapperTest.php

Walkthrough

This PR introduces WrappedExceptionUnwrapper, a utility class that recursively unwraps Symfony Messenger wrapped exceptions. The unwrapper is integrated into both CommandController and ApiExceptionSubscriber through service configuration, replacing their inline exception-handling logic with a centralized, testable implementation.

Changes

Exception Unwrapping Refactor

Layer / File(s) Summary
Exception Unwrapper Contract
src/Exception/WrappedExceptionUnwrapper.php
New WrappedExceptionUnwrapper class with unwrap(Throwable $throwable): Throwable method that recursively unwraps WrappedExceptionsInterface exceptions to extract the root cause.
Service Configuration
config/controller.php, config/subscribers.php
WrappedExceptionUnwrapper is registered as a service and injected into CommandController and ApiExceptionSubscriber constructors.
CommandController Integration
src/Controller/CommandController.php
Constructor dependency updated to accept WrappedExceptionUnwrapper; exception handler now calls unwrap() before throwing the root cause exception.
ApiExceptionSubscriber Integration
src/EventSubscriber/ApiExceptionSubscriber.php
Constructor dependency updated to accept WrappedExceptionUnwrapper; exception is unwrapped before transformation to RFC 7807 response.
CommandController Tests
tests/Unit/Controller/CommandControllerTest.php
All test cases updated to instantiate CommandController with WrappedExceptionUnwrapper dependency.
ApiExceptionSubscriber Tests
tests/Unit/EventSubscriber/ApiExceptionSubscriberTest.php
All test cases updated to instantiate ApiExceptionSubscriber with WrappedExceptionUnwrapper dependency.
WrappedExceptionUnwrapper Tests
tests/Unit/Exception/WrappedExceptionUnwrapperTest.php
New test suite verifies unwrapping behavior for non-wrapped exceptions, HandlerFailedException (single and multiple causes), nested wrapped exceptions, and DelayedMessageHandlingException.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • stixx/openapi-command-bundle#19: Both PRs modify ApiExceptionSubscriber to unwrap Messenger's HandlerFailedException before transforming it; this PR factors that logic into a new WrappedExceptionUnwrapper utility.

Poem

🐰 A tale of exceptions unwrapped with care,
Where nested Messenger errors dance through the air,
Now unified logic extracts the root cause,
No more confusion, no more fuss!
One unwrapper to rule them all! 🎯

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.88% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately summarizes the main change: creating a centralized WrappedExceptionUnwrapper service to handle unwrapping of Messenger exceptions across both CommandController and ApiExceptionSubscriber.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch centralize-wrapped-exception-unwrap

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
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/Unit/Exception/WrappedExceptionUnwrapperTest.php (1)

40-97: ⚡ Quick win

Consolidate similar unwrap cases with a data provider.

The unwrap scenario tests are strong, but these closely related cases can be merged into one parameterized test to reduce duplication and align with the test standard.

♻️ Suggested refactor sketch
+#[DataProvider('wrappedExceptionProvider')]
+public function testUnwrapsWrappedExceptions(Throwable $wrapper, Throwable $expected): void
+{
+    // Arrange
+    $unwrapper = new WrappedExceptionUnwrapper();
+
+    // Act
+    $result = $unwrapper->unwrap($wrapper);
+
+    // Assert
+    self::assertSame($expected, $result);
+}
+
+/**
+ * `@return` iterable<string, array{0: Throwable, 1: Throwable}>
+ */
+public static function wrappedExceptionProvider(): iterable
+{
+    $singleCause = new RuntimeException('the real cause');
+    yield 'handler failed single cause' => [
+        new HandlerFailedException(new Envelope(new stdClass()), [$singleCause]),
+        $singleCause,
+    ];
+
+    $first = new RuntimeException('first handler');
+    $second = new LogicException('second handler');
+    yield 'handler failed multiple causes returns first' => [
+        new HandlerFailedException(new Envelope(new stdClass()), [$first, $second]),
+        $first,
+    ];
+
+    $root = new RuntimeException('root');
+    $inner = new HandlerFailedException(new Envelope(new stdClass()), [$root]);
+    yield 'nested handler failed recursive unwrap' => [
+        new HandlerFailedException(new Envelope(new stdClass()), [$inner]),
+        $root,
+    ];
+}
As per coding guidelines, "Use #[DataProvider] for multiple similar scenarios."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Unit/Exception/WrappedExceptionUnwrapperTest.php` around lines 40 - 97,
Replace the four separate tests that exercise similar unwrap logic with a single
parameterized test using PHPUnit's #[DataProvider] attribute: create a data
provider method (e.g., provideWrappedExceptionScenarios) that yields arrays of
[wrappedExceptionInstance, expectedCause] covering the cases in the existing
tests (single HandlerFailedException with one cause, HandlerFailedException with
multiple causes returning the first, nested HandlerFailedException returning the
inner root cause, and DelayedMessageHandlingException with a single cause), then
implement one test method (e.g., testUnwrapsWrappedExceptionScenarios) that
accepts the two parameters, calls WrappedExceptionUnwrapper::unwrap on the
provided exception and asserts same() against the expected cause; reference
class/constructor symbols HandlerFailedException,
DelayedMessageHandlingException, Envelope and WrappedExceptionUnwrapper when
building the provider cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/Unit/Exception/WrappedExceptionUnwrapperTest.php`:
- Around line 40-97: Replace the four separate tests that exercise similar
unwrap logic with a single parameterized test using PHPUnit's #[DataProvider]
attribute: create a data provider method (e.g.,
provideWrappedExceptionScenarios) that yields arrays of
[wrappedExceptionInstance, expectedCause] covering the cases in the existing
tests (single HandlerFailedException with one cause, HandlerFailedException with
multiple causes returning the first, nested HandlerFailedException returning the
inner root cause, and DelayedMessageHandlingException with a single cause), then
implement one test method (e.g., testUnwrapsWrappedExceptionScenarios) that
accepts the two parameters, calls WrappedExceptionUnwrapper::unwrap on the
provided exception and asserts same() against the expected cause; reference
class/constructor symbols HandlerFailedException,
DelayedMessageHandlingException, Envelope and WrappedExceptionUnwrapper when
building the provider cases.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d2374ce2-c659-48d4-863a-376674b95202

📥 Commits

Reviewing files that changed from the base of the PR and between 1b0fb87 and 1621540.

📒 Files selected for processing (8)
  • config/controller.php
  • config/subscribers.php
  • src/Controller/CommandController.php
  • src/EventSubscriber/ApiExceptionSubscriber.php
  • src/Exception/WrappedExceptionUnwrapper.php
  • tests/Unit/Controller/CommandControllerTest.php
  • tests/Unit/EventSubscriber/ApiExceptionSubscriberTest.php
  • tests/Unit/Exception/WrappedExceptionUnwrapperTest.php

@stixx stixx merged commit 70ef687 into main May 9, 2026
6 checks passed
@stixx stixx deleted the centralize-wrapped-exception-unwrap branch May 9, 2026 23:02
@stixx stixx added the enhancement New feature or request label May 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant