Skip to content

Conversation

@ljonesfl
Copy link
Member

@ljonesfl ljonesfl commented Jan 7, 2026

Note

Adds standardized API error handling and 401/403 HTTP flow with events and views.

  • Introduces RFC 9457 Problem Details helpers via ApiResponseTrait and ProblemDetailsResponse for consistent JSON errors (validation, auth, permissions, rate limit, etc.)
  • Updates Application to catch Unauthorized/Forbidden, emit Http401/Http403 events, and render via HttpCodes@code401/403 (prevents recursion); retains existing 404/500 handling
  • Adds Http401/Http403 events and views resources/views/http_codes/401.php and 403.php
  • Includes examples/http-status-demo.php showcasing events and handling
  • Adds extensive unit/integration tests for Problem Details, events, rate limits, and output buffering; introduces phpunit-no-risky.xml config

Written by Cursor Bugbot for commit 83cd608. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • New Features

    • Added user-facing 401 (Authentication Required), 403 (Access Forbidden) and enhanced 404 pages with contextual details (route, realm, resource, permission)
    • Standardized API error responses using RFC 9457 Problem Details for consistent JSON error output
    • Emitted HTTP error events to enable logging and integrations; included a demo script showcasing event emission and logging
  • Tests

    • Added extensive unit and integration tests plus debug scripts covering Problem Details responses, rate-limiting, HTTP error events, and output buffering
  • Chores

    • Added a PHPUnit configuration for running tests without risky checks

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 7, 2026

📝 Walkthrough

Walkthrough

Adds RFC 9457 Problem Details API helpers and response class, introduces Http401/Http403 event types, extends Application error handling to emit events and route to new 401/403 pages, adds controller/view templates, example/demo, PHPUnit config, and multiple tests for these behaviors.

Changes

Cohort / File(s) Summary
HTTP Event Classes
src/Mvc/Events/Http401.php, src/Mvc/Events/Http403.php
New IEvent implementations: Http401 (string $route, ?string $realm) and Http403 (string $route, ?string $resource, ?string $permission) with constructors and public properties.
Application error handling
src/Mvc/Application.php
Added catch/handling for Unauthorized and Forbidden exceptions: recursion guard, logging, emit Http401/Http403 events, and forward to HttpCodes controller with contextual params.
HTTP Status Controller & Views
src/Mvc/Controllers/HttpCodes.php, resources/views/http_codes/401.php, resources/views/http_codes/403.php
Added code401 and code403 methods; new view templates rendering 401/403 pages using escaped route, optional realm, and optional resource/permission.
API Problem Details infra
src/Mvc/Api/ApiResponseTrait.php, src/Mvc/Api/ProblemDetailsResponse.php
New ApiResponseTrait with helpers for Problem Details (validation, not-found, auth, permission, rate-limit, etc.) and ProblemDetailsResponse class (factory, headers, toJson/send, static helpers).
Examples, config, debug scripts
examples/http-status-demo.php, phpunit-no-risky.xml, tests/debug_output_buffer.php
New demo script illustrating exception→event→emit flow and EventLogger; new PHPUnit config; debug script for output buffering.
Tests — API & Integration
tests/Api/* (multiple), tests/Api/ProblemDetailsIntegrationTest.php, tests/Api/ProblemDetailsResponseTest.php
New unit/integration tests covering ApiResponseTrait, ProblemDetailsResponse, rate-limit helper, and end-to-end Problem Details scenarios.
Tests — MVC / Events / Buffering
tests/Mvc/EventsTest.php, tests/Mvc/SimpleHttpStatusTest.php, tests/Mvc/SimpleOutputBufferTest.php, tests/Mvc/ApplicationTest.php, tests/debug_output_buffer.php
Added/updated tests for Http401/Http403 events, Unauthorized/Forbidden exceptions, output buffer handling; ApplicationTest now preserves initial buffer level.
Example/demo script
examples/http-status-demo.php
New self-contained demo showing throw/catch of Unauthorized/Forbidden/NotFound, emitting Http401/Http403/Http404 events, and logging via EventLogger listener.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client as Client
    participant App as Application
    participant Controller as Controller
    participant EventBus as Event Registry
    participant HttpCodes as HttpCodes Controller
    participant View as View Renderer
    rect rgba(200,230,255,0.5)
      Note right of App: Normal request flow
    end
    Client->>App: HTTP request
    App->>Controller: executeController()
    alt Controller executes successfully
      Controller-->>App: Response (200/2xx)
      App-->>Client: HTTP response
    else Controller throws Unauthorized/Forbidden/NotFound
      Controller-->>App: throw Unauthorized/Forbidden/NotFound
      App->>App: check recursion guard
      App->>EventBus: emit Http401/Http403/Http404(route, realm/resource/permission)
      EventBus->>EventBus: notify registered listeners
      App->>App: log warning
      App->>HttpCodes: forward to code401/code403/code404 with params
      HttpCodes->>View: render corresponding template
      View-->>HttpCodes: HTML body
      HttpCodes-->>App: error response (401/403/404)
      App-->>Client: HTTP error response
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped through code where status sings,

Emitted events and routing wings.
401 and 403, tidy and neat,
Problems boxed with headers complete.
This rabbit nods — the flow's replete.

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'tests' is too vague and generic to meaningfully convey the scope of changes, which span authentication, authorization, API error responses, events, controllers, and views. Use a more descriptive title that captures the main change, such as 'Add RFC 9457 API error responses and HTTP 401/403 event handling' or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@codecov
Copy link

codecov bot commented Jan 7, 2026

Codecov Report

❌ Patch coverage is 77.57848% with 50 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/Mvc/Application.php 22.22% 28 Missing ⚠️
src/Mvc/Controllers/HttpCodes.php 0.00% 19 Missing ⚠️
src/Mvc/Api/ProblemDetailsResponse.php 94.91% 3 Missing ⚠️
Flag Coverage Δ Complexity Δ
mvc 58.18% <77.57%> (+4.95%) 946.00 <42.00> (+47.00)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ Complexity Δ
src/Mvc/Api/ApiResponseTrait.php 100.00% <100.00%> (ø) 23.00 <23.00> (?)
src/Mvc/Events/Http401.php 100.00% <100.00%> (ø) 1.00 <1.00> (?)
src/Mvc/Events/Http403.php 100.00% <100.00%> (ø) 1.00 <1.00> (?)
src/Mvc/Api/ProblemDetailsResponse.php 94.91% <94.91%> (ø) 15.00 <15.00> (?)
src/Mvc/Controllers/HttpCodes.php 47.22% <0.00%> (-52.78%) 4.00 <2.00> (+2.00) ⬇️
src/Mvc/Application.php 70.85% <22.22%> (+29.13%) 77.00 <0.00> (+5.00)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @src/Mvc/Api/ApiResponseTrait.php:
- Around line 186-202: rateLimitProblem currently builds an $extensions array
(with limit and remaining) but never uses it; update the method to merge those
extensions into the ProblemDetailsResponse before sending: create the response
via ProblemDetailsResponse::rateLimitExceeded($retryAfter, $detail), then attach
$extensions to that response's ProblemDetails (either by passing $extensions
into rateLimitExceeded if it supports an extensions arg, or by calling the
response's method to set/merge extensions on the ProblemDetails) and finally
call ->send(); keep the $extensions building logic and ensure limit/remaining
values are included in the sent problem details.

In @tests/Mvc/ApplicationTest.php:
- Around line 31-32: The captured initial output buffer level
($this->initialBufferLevel) in ApplicationTest::setUp() is unused; either remove
the property declaration and the assignment (the ob_get_level() call) to avoid
dead code, or implement buffer restoration in ApplicationTest::tearDown() by
comparing ob_get_level() to $this->initialBufferLevel and calling
ob_end_clean()/ob_end_flush() in a loop until levels match; update or remove the
$initialBufferLevel property and ensure setUp()/tearDown() are consistent.
- Line 22: The property initialBufferLevel is declared but never used; either
remove the unused property declaration or use it by storing the initial buffer
value in setUp() (as currently done) and asserting/restoring it in tearDown() —
e.g., reference initialBufferLevel and the buffer inspection/reset logic in
tearDown() to validate that buffer level returned to initialBufferLevel after
each test, or simply delete the private int $initialBufferLevel field if no
validation is required.
🧹 Nitpick comments (8)
tests/debug_output_buffer.php (1)

1-56: Consider converting this debug script to a proper PHPUnit test.

This standalone debug script duplicates some functionality that could be covered by SimpleOutputBufferTest. If it's meant for one-time debugging, consider removing it before merging. If it serves an ongoing purpose, document its usage in the file header or convert it to a proper test case.

Also, the variable $Ini on line 19 uses PascalCase which is unconventional for PHP variables; consider renaming to $ini or $config for consistency.

tests/Mvc/Events/EventsTest.php (1)

17-100: LGTM!

Comprehensive test coverage for Http401 and Http403 events, including edge cases with null values and various property combinations. The tests properly verify both constructor behavior and property accessibility.

One optional improvement: consider using PHPUnit's @dataProvider annotation for the loop-based tests (e.g., testHttp401EventWithDifferentRealms) to get better test isolation and more descriptive failure messages.

src/Mvc/Application.php (1)

307-311: Simplify the error handler check - double-backslash variant is unnecessary.

The check for both 'Neuron\Mvc\Controllers\HttpCodes' and 'Neuron\\Mvc\\Controllers\\HttpCodes' is redundant. PHP class name comparisons use the normalized form (single backslash). The double-backslash is only needed in string literals for escaping, but the actual value stored is still single-backslash.

♻️ Suggested simplification
 		// Check if we're already handling an error to prevent infinite recursion
-		$isErrorHandler = in_array( $controller, [
-			'Neuron\Mvc\Controllers\HttpCodes',
-			'Neuron\\Mvc\\Controllers\\HttpCodes'
-		] );
+		$isErrorHandler = $controller === 'Neuron\Mvc\Controllers\HttpCodes';
tests/Mvc/SimpleOutputBufferTest.php (2)

12-19: Remove error_log call from test.

The error_log on line 18 will write to stderr during test runs, adding noise to the test output. If debugging info is needed, use PHPUnit's --debug flag or $this->addToAssertionCount() pattern instead.

♻️ Suggested fix
 	public function testInitialBufferLevel(): void
 	{
 		$level = ob_get_level();
 		$this->assertGreaterThanOrEqual( 0, $level );
-
-		// PHPUnit typically starts with level 1
-		error_log( "Initial buffer level in test: $level" );
 	}

21-25: Remove the no-op test.

testNoOutputBuffer asserts assertTrue(true) which provides no test value. If it's a placeholder, remove it; if it's meant to verify something specific about running without buffers, add meaningful assertions.

♻️ Suggested fix
-	public function testNoOutputBuffer(): void
-	{
-		// This test does nothing with output buffers
-		$this->assertTrue( true );
-	}
-
tests/Api/ApiResponseTraitTest.php (1)

30-60: Unused $headers parameter in test helper method.

The $headers parameter on line 37 is declared but never used in the method body. This was flagged by PHPMD.

Additionally, the test helper methods duplicate the logic from ApiResponseTrait rather than delegating to the trait's protected methods. This means the tests verify the expected JSON structure but don't actually exercise the real trait implementation. Consider whether this is intentional (testing the contract) or if the helpers should call the trait methods directly.

🔧 Suggested fix for unused parameter
 			public function testProblemResponse(
 				ProblemType|string $type,
 				?string $title = null,
 				?int $status = null,
 				?string $detail = null,
 				?string $instance = null,
-				array $extensions = [],
-				array $headers = []
+				array $extensions = []
 			): string {
src/Mvc/Api/ProblemDetailsResponse.php (1)

122-125: Handle potential json_encode failure.

json_encode() can return false on failure (e.g., for non-UTF-8 strings or resources). The current implementation doesn't handle this case, which could propagate false to callers expecting a string.

🔧 Suggested fix
 	public function toJson(int $options = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE): string
 	{
-		return json_encode($this->problemDetails, $options);
+		$json = json_encode($this->problemDetails, $options);
+		if ($json === false) {
+			// Fallback to a minimal valid response
+			return json_encode([
+				'type' => '/errors/internal',
+				'title' => 'Internal Server Error',
+				'status' => 500,
+				'detail' => 'Failed to encode error response'
+			]);
+		}
+		return $json;
 	}
src/Mvc/Api/ApiResponseTrait.php (1)

7-7: Unused import.

HttpResponseStatus is imported but never used in this trait.

🔧 Suggested fix
 use Neuron\Core\ProblemDetails\ProblemDetails;
 use Neuron\Core\ProblemDetails\ProblemType;
-use Neuron\Mvc\Responses\HttpResponseStatus;
 use Exception;
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af7c6c5 and 52f3b9d.

📒 Files selected for processing (16)
  • examples/http-status-demo.php
  • phpunit-no-risky.xml
  • resources/views/http_codes/401.php
  • resources/views/http_codes/403.php
  • src/Mvc/Api/ApiResponseTrait.php
  • src/Mvc/Api/ProblemDetailsResponse.php
  • src/Mvc/Application.php
  • src/Mvc/Controllers/HttpCodes.php
  • src/Mvc/Events/Http401.php
  • src/Mvc/Events/Http403.php
  • tests/Api/ApiResponseTraitTest.php
  • tests/Mvc/ApplicationTest.php
  • tests/Mvc/Events/EventsTest.php
  • tests/Mvc/SimpleHttpStatusTest.php
  • tests/Mvc/SimpleOutputBufferTest.php
  • tests/debug_output_buffer.php
🧰 Additional context used
🧬 Code graph analysis (9)
tests/Mvc/SimpleHttpStatusTest.php (2)
src/Mvc/Events/Http401.php (1)
  • Http401 (16-30)
src/Mvc/Events/Http403.php (1)
  • Http403 (16-33)
src/Mvc/Events/Http403.php (1)
src/Mvc/Events/Http401.php (1)
  • __construct (25-29)
tests/Mvc/Events/EventsTest.php (3)
src/Mvc/Events/Http401.php (1)
  • Http401 (16-30)
src/Mvc/Events/Http403.php (1)
  • Http403 (16-33)
examples/http-status-demo.php (1)
  • event (27-49)
src/Mvc/Events/Http401.php (1)
src/Mvc/Events/Http403.php (1)
  • __construct (27-32)
src/Mvc/Controllers/HttpCodes.php (3)
src/Mvc/Requests/Request.php (3)
  • Request (21-385)
  • getRouteParameters (55-58)
  • getRouteParameter (66-69)
src/Mvc/Controllers/IController.php (1)
  • renderHtml (31-31)
src/Mvc/Controllers/Base.php (1)
  • renderHtml (154-167)
src/Mvc/Application.php (3)
src/Mvc/Controllers/Factory.php (1)
  • Factory (15-56)
src/Mvc/Events/Http401.php (1)
  • Http401 (16-30)
src/Mvc/Events/Http403.php (1)
  • Http403 (16-33)
src/Mvc/Api/ApiResponseTrait.php (1)
src/Mvc/Api/ProblemDetailsResponse.php (6)
  • ProblemDetailsResponse (37-231)
  • create (66-69)
  • send (136-149)
  • validationError (196-207)
  • notFound (216-230)
  • rateLimitExceeded (175-187)
tests/debug_output_buffer.php (1)
src/Mvc/Application.php (3)
  • Application (37-837)
  • setCaptureOutput (93-97)
  • getOutput (110-113)
examples/http-status-demo.php (4)
src/Mvc/Events/Http401.php (1)
  • Http401 (16-30)
src/Mvc/Events/Http403.php (1)
  • Http403 (16-33)
src/Mvc/Events/Http404.php (1)
  • Http404 (11-19)
src/Mvc/Application.php (1)
  • Application (37-837)
🪛 PHPMD (2.15.0)
tests/Api/ApiResponseTraitTest.php

37-37: Avoid unused parameters such as '$headers'. (undefined)

(UnusedFormalParameter)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (23)
phpunit-no-risky.xml (1)

1-19: LGTM! Configuration is well-structured.

The PHPUnit configuration appropriately relaxes strictness checks as indicated by the filename, includes a properly configured test suite, and sets up code coverage for the src directory. The bootstrap path and directory structure align with standard PHP project conventions.

src/Mvc/Events/Http401.php (1)

1-30: LGTM! Event class is well-structured and documented.

The Http401 event class follows a clean design pattern consistent with the related Http403 event. The documentation clearly explains when the event is emitted, the properties are appropriately typed, and the optional $realm parameter provides flexibility for WWW-Authenticate header scenarios.

resources/views/http_codes/403.php (1)

1-14: LGTM! View template implements proper security measures.

The template correctly uses htmlspecialchars() with ENT_QUOTES and UTF-8 encoding on all dynamic values, providing solid XSS protection. The conditional rendering for optional $resource and $permission prevents displaying empty fields, and the null coalescing operator provides a sensible fallback for the route.

resources/views/http_codes/401.php (1)

1-9: LGTM! View template follows secure coding practices.

The template properly escapes all dynamic content using htmlspecialchars() with ENT_QUOTES and UTF-8 encoding, preventing XSS vulnerabilities. The structure is consistent with the 403 view template, and the conditional rendering of the realm provides a clean user experience.

tests/Mvc/ApplicationTest.php (1)

72-72: The /test route is properly configured to accept POST requests in examples/config/routes.yaml. The test change to use POST is correct and no issues exist.

Likely an incorrect or invalid review comment.

src/Mvc/Controllers/HttpCodes.php (2)

24-37: LGTM!

The code401 method follows the established pattern from existing error handlers. The implementation correctly renders the 401 view with the authentication realm context.


39-53: LGTM!

The code403 method is consistent with the existing error handler pattern. It properly passes the resource and permission context to the view for meaningful error messages.

tests/Mvc/SimpleHttpStatusTest.php (4)

84-92: Verify constructor argument order for exception chaining.

The Forbidden constructor call uses positional arguments ('Access denied', null, null, 403, $previous). Ensure this matches the constructor signature: (message, resource, permission, code, previous). The Unauthorized call ('Auth failed', null, 401, $previous) should match (message, realm, code, previous).


17-64: LGTM!

Good test coverage for exception and event property accessors, including edge cases with null values and inheritance verification.


75-82: Verify that Forbidden defaults to code 403.

The test calls new Forbidden('Cannot delete') with only the message parameter but expects exception code 403. Since the Forbidden exception class is defined in the external neuron-php/core package and is not accessible in this repository, the default exception code cannot be verified. Confirm that the Forbidden constructor indeed defaults to code 403 when the code parameter is omitted.


66-73: The test correctly assumes Unauthorized defaults to code 401.

Line 72 calls new Unauthorized('Invalid token') with only the message parameter, and line 69 expects exception code 401. This test will only pass if the constructor defaults the code parameter to 401. The broader test patterns confirm this is the intended behavior: the constructors support no-argument calls (line 55-56), single message calls (lines 72, 81), and explicit parameter calls with all values specified (lines 17, 28, 87-88).

tests/Mvc/Events/EventsTest.php (1)

5-6: LGTM!

Appropriate imports added for the new event classes being tested.

src/Mvc/Application.php (3)

352-376: LGTM - Unauthorized exception handling.

The implementation correctly:

  1. Checks the recursion guard to prevent infinite loops
  2. Logs the authentication failure
  3. Emits the Http401 event for observability
  4. Redirects to the code401 controller method with realm context

This follows the established pattern from the NotFound handler.


377-403: LGTM - Forbidden exception handling.

Consistent implementation with the Unauthorized handler, properly passing resource and permission context to the error page.


404-424: LGTM - Recursion guard added to NotFound handler.

Good enhancement to add the same recursion protection to the existing NotFound handler for consistency.

examples/http-status-demo.php (1)

1-143: LGTM!

Well-structured demonstration script that clearly illustrates the HTTP 401/403/404 event system. The inline EventLogger class and step-by-step demos effectively document the expected behavior for developers learning the framework.

tests/Mvc/SimpleOutputBufferTest.php (1)

27-38: LGTM!

Good test that properly validates the output buffering workflow - starts a buffer, captures content, cleans up, and verifies the buffer level is restored.

tests/Api/ApiResponseTraitTest.php (1)

176-425: Good test coverage for RFC 9457 Problem Details scenarios.

The test suite provides comprehensive coverage for:

  • Validation problems with/without custom detail
  • Not-found problems with/without identifier
  • Authentication/permission problems with optional extensions
  • Bad request, conflict, and internal error scenarios
  • Both ProblemType enum and custom string types
  • Full parameter combinations including instance and extensions
src/Mvc/Events/Http403.php (1)

16-33: Clean event class implementation.

The Http403 event class follows the same pattern as Http401 (as seen in the relevant code snippets), implements IEvent, and provides appropriate context properties (route, resource, permission) for 403 error handling. The docblock clearly explains the event's purpose and timing.

src/Mvc/Api/ProblemDetailsResponse.php (2)

136-149: Good defensive check for headers_sent().

The send() method correctly guards against setting headers when they've already been sent, preventing PHP warnings. This makes the class suitable for both normal HTTP contexts and testing scenarios.


175-230: Well-designed static factory methods.

The rateLimitExceeded(), validationError(), and notFound() helper methods provide convenient shortcuts for common error scenarios while maintaining RFC 9457 compliance. The Retry-After header handling in rateLimitExceeded() is particularly well done.

src/Mvc/Api/ApiResponseTrait.php (2)

279-301: Good security consideration for debug info.

The internalErrorProblem method properly gates debug information behind the $includeDebug flag, with clear documentation warning about production usage. This prevents accidental exposure of sensitive exception details.


51-109: Well-structured trait with flexible problem response construction.

The trait provides a clean API with problemDetailsResponse for pre-built ProblemDetails objects and problemResponse for inline construction. The handling of both ProblemType enum and string URIs offers good flexibility for both standard and custom error types.

Copy link

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @tests/Api/ApiResponseTraitTest.php:
- Around line 17-171: The anonymous test controller reimplements ProblemDetails
creation instead of invoking the trait, so replace each testX method
(testProblemDetailsResponse, testProblemResponse, testValidationProblem,
testNotFoundProblem, testAuthenticationProblem, testPermissionProblem,
testBadRequestProblem, testConflictProblem, testInternalErrorProblem) to simply
call and return the corresponding trait methods (e.g., return
$this->validationProblem(...), $this->notFoundProblem(...),
$this->authenticationProblem(...), $this->permissionProblem(...),
$this->badRequestProblem(...), $this->conflictProblem(...),
$this->internalErrorProblem(...), and $this->problemResponse() /
$this->problemDetailsResponse()) so tests exercise the actual ApiResponseTrait
behavior; expose any protected trait methods by adding public wrapper methods in
the anonymous class that delegate directly to the trait methods with the same
arguments and return values.
🧹 Nitpick comments (2)
tests/Api/ApiResponseTraitTest.php (1)

432-447: Inconsistent test pattern: closures defined in test methods

Test methods testServiceUnavailableProblem, testServiceUnavailableWithRetryAfter, testMethodNotAllowedProblem, and testUnsupportedMediaTypeProblem dynamically add closure properties to the controller object at runtime. This pattern is inconsistent with the methods defined in setUp() (lines 24-169) and makes the test structure harder to follow.

♻️ Refactor to define methods in setUp

Move these closures into the anonymous class definition in setUp():

 $this->controller = new class {
   use ApiResponseTrait;
   
+  public function testServiceUnavailableProblem(?string $detail = null, ?int $retryAfter = null): string
+  {
+    $extensions = [];
+    if ($retryAfter !== null) {
+      $extensions['retry_after'] = $retryAfter;
+    }
+    $problem = new ProblemDetails(/* ... */);
+    return json_encode($problem);
+  }
   
   // ... other methods
 };

Then in test methods, call directly:

$json = $this->controller->testServiceUnavailableProblem();
tests/Api/ProblemDetailsIntegrationTest.php (1)

22-228: LGTM: Proper integration testing approach

The anonymous controller correctly extends Base, uses ApiResponseTrait, and its action methods (show, create, update, etc.) properly delegate to the actual trait methods like $this->notFoundProblem(), $this->validationProblem(), $this->conflictProblem(), etc. The renderJson override at lines 223-226 appropriately returns JSON for test assertions rather than sending headers.

This integration test validates real-world usage patterns and exercises the trait's actual implementation.

Note: Static analysis flags the unused $responseCode parameter in the renderJson override (line 223). This is acceptable for a test override that simplifies the response, but you could optionally include it in the test output if HTTP status codes need validation.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 395b3c2 and b1557de.

📒 Files selected for processing (3)
  • tests/Api/ApiResponseTraitTest.php
  • tests/Api/ProblemDetailsIntegrationTest.php
  • tests/Api/ProblemDetailsResponseTest.php
✅ Files skipped from review due to trivial changes (1)
  • tests/Api/ProblemDetailsResponseTest.php
🧰 Additional context used
🧬 Code graph analysis (1)
tests/Api/ApiResponseTraitTest.php (1)
src/Mvc/Api/ProblemDetailsResponse.php (1)
  • toArray (159-166)
🪛 PHPMD (2.15.0)
tests/Api/ProblemDetailsIntegrationTest.php

223-223: Avoid unused parameters such as '$responseCode'. (undefined)

(UnusedFormalParameter)

tests/Api/ApiResponseTraitTest.php

37-37: Avoid unused parameters such as '$headers'. (undefined)

(UnusedFormalParameter)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (1)
tests/Api/ProblemDetailsIntegrationTest.php (1)

230-496: Comprehensive integration test coverage

The test suite provides excellent coverage of Problem Details scenarios:

  • CRUD operations with success and error paths
  • Validation errors (simple and nested)
  • Resource not found (404)
  • Conflicts (409)
  • Permission denied (403)
  • Rate limiting (429) with proper sequential request testing
  • Internal errors (500)
  • Custom business errors with extensions
  • Response structure validation ensuring RFC 9457 compliance

The testResponseDistinction test (lines 425-441) is particularly valuable for ensuring clients can reliably differentiate between success responses and error responses.

@ljonesfl ljonesfl closed this Jan 7, 2026
@ljonesfl ljonesfl deleted the feature/new-events branch January 7, 2026 22:56
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.

2 participants