Skip to content

Conversation

@ljonesfl
Copy link
Member

@ljonesfl ljonesfl commented Dec 31, 2025

Summary by CodeRabbit

  • Tests
    • Large expansion of unit tests across services, controllers, widgets and media to improve stability and edge-case coverage.
  • Refactor
    • Controllers and services now enforce explicit dependency injection and fail-fast behavior, improving runtime reliability.
  • Chores
    • Security throttle relocated to a clearer namespace and DI container updated to register new services.

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


Note

Refactor/DI

  • Controllers now require injected SettingManager and SessionManager (and other deps); remove service-locator fallbacks and throw on missing deps
  • Content uses DI-only and reads version file via settings.paths.version_file

Container

  • Register EditorJsRenderer, CloudinaryUploader, MediaValidator, ResendVerificationThrottle; bind IIpResolver to DefaultIpResolver

Security

  • Move ResendVerificationThrottle to Neuron\Cms\Services\Security

Tests

  • Large expansion and updates of unit tests across controllers, services, and media to match new DI and behavior

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 31, 2025

📝 Walkthrough

Walkthrough

Widespread DI refactor: controllers and services now require explicit SettingManager and SessionManager injections (removing container fallbacks); several service constructors accept optional event emitters or custom RNGs; many unit tests added/updated to exercise constructor validation, event emission, and media/widget behaviors.

Changes

Cohort / File(s) Summary
Controller constructor DI changes
src/Cms/Controllers/...
src/Cms/Controllers/Admin/*, src/Cms/Controllers/Member/*, src/Cms/Controllers/Auth/*, src/Cms/Controllers/*
Added SettingManager and SessionManager constructor parameters broadly; removed service-locator fallbacks; constructors now validate required collaborators and throw InvalidArgumentException when null; many nullable properties tightened to non-nullable.
Admin controllers (enforced DI)
src/Cms/Controllers/Admin/*
Admin controllers (Categories, Events, Pages, Posts, Media, Profile, Tags, Users, etc.) updated to require explicit injections; Media now requires CloudinaryUploader and MediaValidator as non-nullable.
Member & public controllers
src/Cms/Controllers/Member/*, src/Cms/Controllers/*
Home, Blog, Calendar, Pages, Content, Member controllers updated to accept settings/session and enforce injection of repositories/services.
Service constructor & event-emitter changes
src/Cms/.../Services/*
e.g. Category/Deleter.php, User/Deleter.php, PasswordResetter.php
Several services now accept optional Emitter and/or optional IRandom parameters; tests added to assert events emitted (Category*/User* Created/Updated/Deleted) and custom RNG usage.
DI container & provider bindings
src/Cms/Container/Container.php, src/Cms/Container/CmsServiceProvider.php
New bindings for EditorJsRenderer, CloudinaryUploader, MediaValidator, ResendVerificationThrottle; ResendVerificationThrottle moved namespace to Services\Security.
Namespace / service relocation
src/Cms/Services/Security/ResendVerificationThrottle.php
Class namespace changed from Neuron\Cms\AuthNeuron\Cms\Services\Security (docblock updated).
Media / Cloudinary tests
tests/Unit/Cms/Services/Media/CloudinaryUploaderTest.php
Large new test suite covering upload (file/URL), delete, list, pagination and error paths; mocks Cloudinary SDK internals for success and failure scenarios.
Widget & renderer tests
tests/Unit/Cms/Services/Widget/*
New CalendarWidget and WidgetRenderer tests covering rendering outputs, repository-missing comments, and HTML structure cases.
Controller tests refactored to DI
tests/Unit/Cms/Controllers/* (many files)
Hundreds of tests updated to construct controllers with SettingManager and SessionManager mocks, remove reflection wiring, and add negative tests asserting InvalidArgumentException on missing dependencies; many test files create temporary .version.json and Memory-backed settings.
Various service unit tests added
tests/Unit/Cms/Services/*
New unit tests for Creator/Updater/Deleter/Verifier/PasswordResetter/User flows validating constructor wiring, not-found exceptions, event emission, and custom RNG injection.
Test bootstrap / fixtures
tests/Unit/... (multiple)
Tests now frequently create temporary .version.json files and register Memory-backed SettingManager in Registry during setUp/tearDown.

Sequence Diagram(s)

sequenceDiagram
    rect rgba(46,134,193,0.06)
    participant Test as Test (unit)
    participant DTO as DTO
    participant Creator as Service\Creator
    participant Repo as Repository
    participant Emitter as EventEmitter
    note right of Emitter `#f6e58d`: optional dependency injected
    end

    Note over Test,DTO: Arrange DTO and mocks
    Test->>Creator: create(dto)
    Creator->>Repo: repository->create(entity)
    Repo-->>Creator: created entity
    alt emitter provided
        Creator->>Emitter: emit(CategoryCreatedEvent)
        Emitter-->>Test: event observed (mock expectation)
    end
    Note over Test: Assert repo called and event emitted (when emitter present)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐇 I hopped through constructors, pads of fluff,

Injected settings, sessions — stuff enough,
I nudged events to sparkle, tokens spun bright,
Mock clouds uploaded under moonlit night,
A rabbit cheers — tests pass on every flight!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.55% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'updates tests' is vague and generic, using non-descriptive language that does not convey meaningful information about the substantial architectural and structural changes in this PR. Consider a more descriptive title that reflects the core changes, such as 'Enforce strict dependency injection in controllers and services' or 'Add required SettingManager/SessionManager to controller constructors and update test coverage'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ 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 Dec 31, 2025

Codecov Report

❌ Patch coverage is 77.83784% with 41 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/Cms/Controllers/Admin/Posts.php 73.68% 5 Missing ⚠️
src/Cms/Controllers/Blog.php 68.75% 5 Missing ⚠️
src/Cms/Controllers/Admin/Events.php 69.23% 4 Missing ⚠️
src/Cms/Controllers/Admin/EventCategories.php 70.00% 3 Missing ⚠️
src/Cms/Controllers/Admin/Pages.php 70.00% 3 Missing ⚠️
src/Cms/Controllers/Admin/Profile.php 70.00% 3 Missing ⚠️
src/Cms/Controllers/Admin/Users.php 76.92% 3 Missing ⚠️
src/Cms/Controllers/Member/Profile.php 70.00% 3 Missing ⚠️
src/Cms/Controllers/Admin/Categories.php 80.00% 2 Missing ⚠️
src/Cms/Controllers/Admin/Media.php 71.42% 2 Missing ⚠️
... and 5 more
Flag Coverage Δ Complexity Δ
cms 75.15% <77.83%> (+2.29%) 2049.00 <2.00> (+51.00)

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

Files with missing lines Coverage Δ Complexity Δ
src/Cms/Container/CmsServiceProvider.php 96.42% <ø> (ø) 7.00 <0.00> (ø)
src/Cms/Container/Container.php 61.53% <100.00%> (+1.94%) 10.00 <0.00> (ø)
src/Cms/Controllers/Admin/Dashboard.php 100.00% <100.00%> (+80.00%) 2.00 <1.00> (ø)
src/Cms/Controllers/Auth/Login.php 29.41% <100.00%> (+2.88%) 18.00 <0.00> (+1.00)
src/Cms/Controllers/Auth/PasswordReset.php 45.58% <100.00%> (+1.64%) 14.00 <0.00> (+1.00)
src/Cms/Controllers/Member/Dashboard.php 100.00% <100.00%> (+100.00%) 2.00 <1.00> (ø)
src/Cms/Controllers/Member/Registration.php 75.29% <100.00%> (+2.56%) 19.00 <0.00> (+4.00)
...s/Services/Security/ResendVerificationThrottle.php 78.72% <ø> (ø) 17.00 <0.00> (?)
src/Cms/Controllers/Content.php 60.78% <91.66%> (-0.05%) 32.00 <0.00> (+2.00) ⬇️
src/Cms/Controllers/Home.php 93.33% <80.00%> (+93.33%) 3.00 <0.00> (+1.00)
... and 13 more

... and 13 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@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: 0

🧹 Nitpick comments (1)
tests/Unit/Cms/Services/EmailVerifierTest.php (1)

374-410: Good dependency injection test.

The test effectively verifies that a custom random generator can be injected and is correctly wired into the token creation flow. The test structure is clear and follows good isolation practices.

Consider optionally enhancing the assertion by capturing and verifying the actual token value passed to create() matches the custom random output. This would provide stronger confidence that the dependency is used correctly throughout the flow.

💡 Optional enhancement to verify token value
 	$tokenRepository
 		->expects( $this->once() )
 		->method( 'create' )
-		->with( $this->isInstanceOf( EmailVerificationToken::class ) );
+		->with( $this->callback( function( $token ) {
+			return $token instanceof EmailVerificationToken &&
+			       $token->getToken() === hash( 'sha256', str_repeat( 'a', 64 ) );
+		} ) );
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 154b61f and d1c64a9.

📒 Files selected for processing (9)
  • tests/Unit/Cms/Services/Category/CreatorTest.php
  • tests/Unit/Cms/Services/Category/UpdaterTest.php
  • tests/Unit/Cms/Services/EmailVerifierTest.php
  • tests/Unit/Cms/Services/Event/UpdaterTest.php
  • tests/Unit/Cms/Services/EventCategory/UpdaterTest.php
  • tests/Unit/Cms/Services/Page/UpdaterTest.php
  • tests/Unit/Cms/Services/PasswordResetterTest.php
  • tests/Unit/Cms/Services/User/CreatorTest.php
  • tests/Unit/Cms/Services/User/UpdaterTest.php
🧰 Additional context used
🧬 Code graph analysis (7)
tests/Unit/Cms/Services/User/CreatorTest.php (1)
src/Cms/Events/UserCreatedEvent.php (1)
  • UserCreatedEvent (13-23)
tests/Unit/Cms/Services/Category/CreatorTest.php (1)
src/Cms/Events/CategoryCreatedEvent.php (1)
  • CategoryCreatedEvent (13-23)
tests/Unit/Cms/Services/Category/UpdaterTest.php (1)
src/Cms/Events/CategoryUpdatedEvent.php (1)
  • CategoryUpdatedEvent (13-23)
tests/Unit/Cms/Services/Page/UpdaterTest.php (2)
tests/Unit/Cms/Services/Category/UpdaterTest.php (1)
  • testConstructorSetsPropertiesCorrectly (208-215)
tests/Unit/Cms/Services/User/UpdaterTest.php (1)
  • testConstructorSetsPropertiesCorrectly (255-263)
tests/Unit/Cms/Services/PasswordResetterTest.php (3)
src/Cms/Auth/PasswordHasher.php (1)
  • hash (24-30)
src/Cms/Models/PasswordResetToken.php (1)
  • PasswordResetToken (14-178)
src/Cms/Services/Auth/PasswordResetter.php (1)
  • PasswordResetter (23-244)
tests/Unit/Cms/Services/Event/UpdaterTest.php (1)
tests/Unit/Cms/Services/EventCategory/UpdaterTest.php (1)
  • test_constructor_sets_properties_correctly (284-291)
tests/Unit/Cms/Services/User/UpdaterTest.php (2)
src/Cms/Models/User.php (3)
  • setUsername (100-104)
  • setRole (151-155)
  • setPasswordHash (134-138)
src/Cms/Events/UserUpdatedEvent.php (1)
  • UserUpdatedEvent (13-23)
⏰ 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). (3)
  • GitHub Check: build-test (sqlite)
  • GitHub Check: build-test (mysql)
  • GitHub Check: build-test (postgres)
🔇 Additional comments (19)
tests/Unit/Cms/Services/Page/UpdaterTest.php (2)

268-275: LGTM!

The constructor test is straightforward and follows the same pattern as similar tests in Category/UpdaterTest.php and User/UpdaterTest.php.


277-299: LGTM!

The not-found exception test properly configures the mock to return null and verifies that the expected exception with the correct message is thrown. This aligns with the broader PR pattern of adding error handling tests across Updater services.

tests/Unit/Cms/Services/PasswordResetterTest.php (2)

277-303: LGTM!

This test covers an important edge case where a user is deleted after a password reset token was created. The test correctly verifies that resetPassword returns false when the token is valid but the associated user no longer exists.


305-341: LGTM!

This test properly validates the custom IRandom dependency injection. It verifies that:

  1. The custom random generator is used (via expects($this->once()))
  2. The correct parameters are passed (64, 'hex')
  3. A PasswordResetToken is created with the generated token

The test aligns with the PasswordResetter constructor signature that accepts an optional IRandom parameter.

tests/Unit/Cms/Services/Category/UpdaterTest.php (3)

208-215: LGTM!

Standard constructor instantiation test following the established pattern.


217-248: LGTM!

The test correctly validates that CategoryUpdatedEvent is emitted when an event emitter is injected. The null second parameter suggests there's an intermediate dependency (possibly a slug generator or similar) between the repository and event emitter in the constructor signature.


250-268: LGTM!

The not-found exception test properly validates error handling when attempting to update a non-existent category.

tests/Unit/Cms/Services/Category/CreatorTest.php (2)

159-166: LGTM!

Standard constructor instantiation test.


168-191: LGTM!

The test properly validates that CategoryCreatedEvent is emitted when creating a category with an injected event emitter. The constructor signature with (categoryRepository, null, eventEmitter) is consistent with the UpdaterTest pattern in the same module.

tests/Unit/Cms/Services/Event/UpdaterTest.php (2)

416-424: LGTM!

Constructor test follows the established snake_case naming convention used throughout this test file.


426-444: Consider exception type consistency across services.

This test expects \RuntimeException, while similar tests in Page/UpdaterTest and Category/UpdaterTest expect \Exception. If this difference is intentional (e.g., the Event Updater implementation throws RuntimeException), this is fine. Otherwise, consider aligning exception types across all Updater services for consistency.

tests/Unit/Cms/Services/EventCategory/UpdaterTest.php (2)

284-291: LGTM!

Constructor test follows the snake_case naming pattern established in this test file.


293-311: LGTM!

The not-found exception test properly verifies error handling. The use of \RuntimeException is consistent with the Event/UpdaterTest in the same domain area.

tests/Unit/Cms/Services/User/CreatorTest.php (2)

238-246: LGTM!

Standard constructor instantiation test following the camelCase naming convention used in this test file.


248-282: LGTM!

The test comprehensively validates event emission when creating a user with an injected event emitter. The mock setup properly configures PasswordHasher requirements and hash generation, ensuring the full create() flow executes to trigger the event.

tests/Unit/Cms/Services/User/UpdaterTest.php (3)

255-263: LGTM!

Standard constructor instantiation test.


265-301: LGTM!

The test properly validates UserUpdatedEvent emission with an injected event emitter. The user entity is correctly configured with all required fields before the update operation.


303-322: LGTM!

The not-found exception test properly validates error handling when updating a non-existent user. Uses \Exception which is consistent with other domain services like Page and Category.

tests/Unit/Cms/Services/EmailVerifierTest.php (1)

346-372: Test correctly validates the implementation behavior.

The test accurately reflects the current implementation: when a token exists but the user is not found (deleted), verifyEmail() returns false without deleting the token. The test properly expects no call to deleteByToken(), which matches lines 143-146 of the EmailVerifier implementation.

The original concern about orphaned tokens is a valid architectural consideration, but the test correctly validates the existing behavior. If cleanup of orphaned tokens is desired, that would require changes to the implementation, not the test.

Copy link
Contributor

@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

♻️ Duplicate comments (3)
tests/Unit/Cms/Controllers/CalendarTest.php (1)

35-41: Same file path and error suppression issues as in PagesTest.

This test has the same brittle file path assumption and error suppression issues identified in PagesTest.php. Consider extracting the common setup/teardown logic to a base test class to reduce duplication and ensure consistent fixes.

💡 Suggestion: Create base test class

Create a base class like BaseControllerTestCase that handles common setup/teardown:

abstract class BaseControllerTestCase extends TestCase
{
    protected SettingManager $_settingManager;
    protected string $_tempDir;

    protected function setUp(): void
    {
        parent::setUp();
        
        // Create temporary directory for test artifacts
        $this->_tempDir = sys_get_temp_dir() . '/neuron_test_' . uniqid();
        mkdir( $this->_tempDir, 0777, true );
        
        // Setup settings
        $settings = new Memory();
        $settings->set( 'site', 'name', 'Test Site' );
        // ... rest of settings
        
        $this->_settingManager = new SettingManager( $settings );
        Registry::getInstance()->set( 'Settings', $this->_settingManager );
        
        // Create version file in temp directory
        file_put_contents( $this->_tempDir . '/.version.json', 
            json_encode([ 'major' => 1, 'minor' => 0, 'patch' => 0 ]) );
    }

    protected function tearDown(): void
    {
        // Clear registry
        Registry::getInstance()->set( 'Settings', null );
        Registry::getInstance()->set( 'version', null );
        // ... rest of cleanup
        
        // Clean up temp directory
        if( is_dir( $this->_tempDir ) )
        {
            array_map( 'unlink', glob( "$this->_tempDir/*" ) );
            rmdir( $this->_tempDir );
        }
        
        parent::tearDown();
    }
}

Also applies to: 52-53

tests/Unit/Cms/Controllers/HomeTest.php (1)

35-46: Same file path and error suppression issues.

This test file has the same issues identified in PagesTest.php and CalendarTest.php. Refer to those comments for details and the suggestion to create a base test class.

Also applies to: 58-60

tests/Unit/Cms/Controllers/Admin/DashboardTest.php (1)

32-38: Same file path and error suppression issues.

This file now uses the same setup/teardown pattern as the other test files, with the same brittle file path assumption and error suppression issues. See comments in PagesTest.php for details.

Also applies to: 50-51

🧹 Nitpick comments (13)
tests/Unit/Cms/Services/User/DeleterTest.php (2)

84-91: Test name could be more descriptive.

The test name suggests it verifies properties are set correctly, but it only checks that the constructor instantiates the class without throwing an exception. While this pattern is consistent with similar tests in the codebase (e.g., CategoryUpdaterTest), consider either:

  • Renaming to testConstructorInstantiatesCorrectly() for accuracy
  • Or enhancing the test to actually verify that the repository property is correctly set

93-118: LGTM! Consider verifying event payload.

The test correctly verifies that a UserDeletedEvent is emitted when the event emitter is provided. The implementation follows the same pattern as similar tests in the codebase (CategoryDeleterTest, CreatorTest, UpdaterTest).

Optionally, you could enhance the test to verify the event's userId property matches the deleted user's ID:

🔎 Optional enhancement to verify event payload
 	// Event emitter should emit UserDeletedEvent
 	$eventEmitter
 		->expects( $this->once() )
 		->method( 'emit' )
-		->with( $this->isInstanceOf( \Neuron\Cms\Events\UserDeletedEvent::class ) );
+		->with( $this->callback( function( $event ) {
+			return $event instanceof \Neuron\Cms\Events\UserDeletedEvent
+				&& $event->userId === 1;
+		} ) );
tests/Unit/Cms/Services/Category/DeleterTest.php (1)

84-91: Test name doesn't match behavior.

The test is named testConstructorSetsPropertiesCorrectly but only asserts that the instance is created. It doesn't verify that any properties are correctly set. Consider renaming to testConstructorCreatesInstance or testCanInstantiateWithRepository to accurately reflect what the test validates.

🔎 Suggested rename
-	public function testConstructorSetsPropertiesCorrectly(): void
+	public function testCanInstantiateWithRepository(): void
 	{
 		$categoryRepository = $this->createMock( ICategoryRepository::class );

 		$deleter = new Deleter( $categoryRepository );

 		$this->assertInstanceOf( Deleter::class, $deleter );
 	}
tests/Unit/Cms/Controllers/Member/DashboardTest.php (2)

32-38: Potential race condition with shared version file.

The test creates .version.json in a shared location that could be accessed by parallel test runs. While this pattern is consistent with other controller tests (Admin Dashboard, Home), it may cause race conditions if multiple tests run concurrently.

Consider using a unique temporary directory per test or leveraging PHPUnit's @tempDir functionality to avoid file system conflicts.

Based on relevant code snippets showing the same pattern in Admin/Home controller tests.


69-98: Consider reducing mocking for stronger test confidence.

The test mocks both the controller and the ViewContext, which means it's verifying that mocks return what they're configured to return rather than testing the actual Dashboard behavior. This pattern is consistent with the Admin Dashboard test, but it provides limited confidence that the real index() method works correctly.

For stronger test coverage, consider either:

  1. Testing the real controller with a mocked ViewContext (remove controller mock)
  2. Adding an integration test that uses the real view pipeline

This would verify that Dashboard::index() actually calls view() with the correct template and chains the fluent methods in the proper order.

Based on relevant code snippets showing the same pattern in Admin Dashboard tests.

tests/Unit/Cms/Services/Media/CloudinaryUploaderTest.php (2)

210-398: Consider extracting the integration test skip logic.

The credential check and skip logic is duplicated across six integration tests (lines 213-222, 244-253, 267-276, 289-298, 328-337, 357-366). This violates the DRY principle and makes the tests harder to maintain.

🔎 Suggested refactor to extract skip logic

Add a helper method to the test class:

/**
 * Skip test if real Cloudinary credentials are not configured
 */
private function skipIfNoRealCredentials(): void
{
    $cloudName = $this->_settings->get( 'cloudinary', 'cloud_name' );
    $isTestCredentials = ($cloudName === 'test-cloud');
    $hasRealCredentials = getenv( 'CLOUDINARY_URL' ) || (!$isTestCredentials && $cloudName);

    if( !$hasRealCredentials )
    {
        $this->markTestSkipped(
            'Cloudinary credentials not configured. Set CLOUDINARY_URL environment variable or configure real cloudinary settings to run this integration test.'
        );
    }
}

Then replace each duplicated block with a single call:

 public function testUploadWithValidFile(): void
 {
-    // Skip if using test credentials (not real Cloudinary account)
-    $cloudName = $this->_settings->get( 'cloudinary', 'cloud_name' );
-    $isTestCredentials = ($cloudName === 'test-cloud');
-    $hasRealCredentials = getenv( 'CLOUDINARY_URL' ) || (!$isTestCredentials && $cloudName);
-
-    if( !$hasRealCredentials )
-    {
-        $this->markTestSkipped(
-            'Cloudinary credentials not configured. Set CLOUDINARY_URL environment variable or configure real cloudinary settings to run this integration test.'
-        );
-    }
+    $this->skipIfNoRealCredentials();

     // Integration test requires actual file and credentials
     ...

Apply the same pattern to all six integration tests.


404-879: Extract the repeated reflection-based mock injection pattern.

The reflection code to inject mocked Cloudinary instances is duplicated across 11 test methods. This duplication reduces maintainability and increases the cognitive load when reading tests.

🔎 Suggested refactor to extract mock injection helper

Add a helper method to the test class:

/**
 * Inject a mocked Cloudinary instance into the uploader
 */
private function injectMockCloudinary( CloudinaryUploader $uploader, $mockCloudinary ): void
{
    $reflection = new \ReflectionClass( $uploader );
    $property = $reflection->getProperty( '_cloudinary' );
    $property->setAccessible( true );
    $property->setValue( $uploader, $mockCloudinary );
}

Then replace each duplicated reflection block:

 public function testUploadCallsCloudinaryApiCorrectly(): void
 {
     // ... create mocks ...

-    // Use reflection to inject the mock
-    $reflection = new \ReflectionClass( $uploader );
-    $property = $reflection->getProperty( '_cloudinary' );
-    $property->setAccessible( true );
-    $property->setValue( $uploader, $mockCloudinary );
+    $this->injectMockCloudinary( $uploader, $mockCloudinary );

     // Test the upload
     $result = $uploader->upload( $testFile );
     ...

Apply this pattern to all 11 tests that use reflection for mock injection (lines 447-451, 513-517, 568-572, 601-605, 629-633, 692-696, 739-743, 777-781, 813-817, 841-845, 869-873).

tests/Unit/Cms/Services/Widget/CalendarWidgetTest.php (1)

345-353: Constructor test is functional but could be enhanced.

The test verifies that the constructor accepts dependencies and returns the correct instance type. While this is valid, it doesn't confirm that the injected repositories are actually stored and accessible.

💡 Optional: Consider verifying dependency usage

If you want to strengthen this test, you could verify that the injected dependencies are actually used by calling a method that requires them:

 public function testConstructorSetsPropertiesCorrectly(): void
 {
 	$eventRepo = $this->createMock( DatabaseEventRepository::class );
 	$categoryRepo = $this->createMock( DatabaseEventCategoryRepository::class );
+	
+	// Verify the repositories are actually used
+	$eventRepo->expects( $this->once() )
+		->method( 'getUpcoming' )
+		->willReturn( [] );
 
 	$widget = new CalendarWidget( $eventRepo, $categoryRepo );
 
 	$this->assertInstanceOf( CalendarWidget::class, $widget );
+	$widget->render( [] ); // Trigger dependency usage
 }

However, this is optional since the existing tests already verify dependency usage in various scenarios.

tests/Unit/Cms/Controllers/Admin/ProfileTest.php (2)

54-54: Avoid error suppression; handle file deletion explicitly.

The @ operator suppresses all errors from unlink(), which can hide legitimate issues like permission problems or I/O errors during cleanup.

🔎 Proposed improvement
-		$parentDir = dirname( getcwd() );
-		@unlink( $parentDir . '/.version.json' );
+		if( isset( $this->_versionFilePath ) && file_exists( $this->_versionFilePath ) )
+		{
+			unlink( $this->_versionFilePath );
+		}

69-78: Consider adding assertions to verify dependency injection.

While the test successfully verifies instantiation, it could be strengthened by confirming that the dependencies are properly stored. If the Profile controller exposes getters or has public properties for its dependencies, consider asserting those values.

Example enhancement (if applicable):

$controller = new Profile( null, $mockRepository, $mockHasher, $mockUpdater );

$this->assertInstanceOf( Profile::class, $controller );
// If getters exist:
// $this->assertSame( $mockRepository, $controller->getRepository() );
// $this->assertSame( $mockHasher, $controller->getHasher() );
// $this->assertSame( $mockUpdater, $controller->getUpdater() );

Note: The comment acknowledging integration testing requirements is helpful documentation.

tests/Unit/Cms/Controllers/Member/ProfileTest.php (1)

69-78: Consider expanding test assertions within unit test constraints.

While the integration test note is appreciated, the current test only verifies that the constructor doesn't throw an exception. Consider adding assertions to verify that the mocked dependencies can be accessed through the controller instance (e.g., via getters or by verifying method calls), provided this doesn't require the complex infrastructure mentioned in the comment.

However, if the Profile controller lacks public accessors and requires full integration context, the current minimal test is acceptable.

tests/Unit/Cms/Controllers/PagesTest.php (1)

61-63: Avoid error suppression operator.

Using @unlink suppresses all errors, which can hide legitimate issues during test execution. Instead, check if the file exists before attempting to delete it.

🔎 Proposed fix
-		$parentDir = dirname( getcwd() );
-		@unlink( $parentDir . '/.version.json' );
+		$parentDir = dirname( getcwd() );
+		$versionFile = $parentDir . '/.version.json';
+		if( file_exists( $versionFile ) )
+		{
+			unlink( $versionFile );
+		}
tests/Unit/Cms/Controllers/HomeTest.php (1)

168-196: Minor test duplication.

This test largely duplicates the assertions from testIndexWithRegistrationEnabled (lines 81-111). Consider whether this separate test adds sufficient value, or if the verification in the earlier test is adequate.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d1c64a9 and f4c00dc.

📒 Files selected for processing (12)
  • tests/Unit/Cms/Controllers/Admin/DashboardTest.php
  • tests/Unit/Cms/Controllers/Admin/ProfileTest.php
  • tests/Unit/Cms/Controllers/CalendarTest.php
  • tests/Unit/Cms/Controllers/HomeTest.php
  • tests/Unit/Cms/Controllers/Member/DashboardTest.php
  • tests/Unit/Cms/Controllers/Member/ProfileTest.php
  • tests/Unit/Cms/Controllers/PagesTest.php
  • tests/Unit/Cms/Services/Category/DeleterTest.php
  • tests/Unit/Cms/Services/Media/CloudinaryUploaderTest.php
  • tests/Unit/Cms/Services/User/DeleterTest.php
  • tests/Unit/Cms/Services/Widget/CalendarWidgetTest.php
  • tests/Unit/Cms/Services/Widget/WidgetRendererTest.php
🧰 Additional context used
🧬 Code graph analysis (9)
tests/Unit/Cms/Controllers/Member/ProfileTest.php (1)
src/Cms/Auth/PasswordHasher.php (1)
  • PasswordHasher (13-204)
tests/Unit/Cms/Services/Widget/CalendarWidgetTest.php (1)
src/Cms/Services/Widget/CalendarWidget.php (1)
  • CalendarWidget (15-147)
tests/Unit/Cms/Services/Widget/WidgetRendererTest.php (2)
src/Cms/Services/Widget/WidgetRenderer.php (1)
  • WidgetRenderer (19-146)
tests/Unit/Cms/Services/Widget/WidgetTest.php (1)
  • render (16-19)
tests/Unit/Cms/Services/User/DeleterTest.php (5)
tests/Unit/Cms/Services/Category/DeleterTest.php (2)
  • testConstructorSetsPropertiesCorrectly (84-91)
  • testConstructorWithEventEmitter (93-118)
tests/Unit/Cms/Services/Category/UpdaterTest.php (2)
  • testConstructorSetsPropertiesCorrectly (208-215)
  • testConstructorWithEventEmitter (217-248)
tests/Unit/Cms/Services/User/CreatorTest.php (2)
  • testConstructorSetsPropertiesCorrectly (238-246)
  • testConstructorWithEventEmitter (248-282)
tests/Unit/Cms/Services/User/UpdaterTest.php (2)
  • testConstructorSetsPropertiesCorrectly (255-263)
  • testConstructorWithEventEmitter (265-301)
src/Cms/Events/UserDeletedEvent.php (1)
  • UserDeletedEvent (12-22)
tests/Unit/Cms/Controllers/Member/DashboardTest.php (3)
tests/Unit/Cms/Controllers/Admin/DashboardTest.php (6)
  • DashboardTest (14-99)
  • setUp (18-39)
  • tearDown (41-54)
  • testConstructor (56-60)
  • testConstructorWithApplication (62-67)
  • testIndexRendersView (73-98)
tests/Unit/Cms/Controllers/HomeTest.php (2)
  • setUp (18-47)
  • tearDown (49-63)
src/Cms/Container/Container.php (1)
  • getInstance (251-254)
tests/Unit/Cms/Controllers/PagesTest.php (1)
src/Cms/Services/Content/EditorJsRenderer.php (1)
  • EditorJsRenderer (12-370)
tests/Unit/Cms/Services/Media/CloudinaryUploaderTest.php (1)
src/Cms/Services/Media/CloudinaryUploader.php (5)
  • CloudinaryUploader (17-371)
  • upload (68-89)
  • uploadFromUrl (99-118)
  • delete (127-140)
  • listResources (149-184)
tests/Unit/Cms/Controllers/Admin/DashboardTest.php (3)
tests/Unit/Cms/Controllers/Member/DashboardTest.php (6)
  • DashboardTest (14-99)
  • setUp (18-39)
  • tearDown (41-54)
  • testConstructor (56-60)
  • testConstructorWithApplication (62-67)
  • testIndexRendersView (73-98)
tests/Unit/Cms/Controllers/HomeTest.php (2)
  • setUp (18-47)
  • tearDown (49-63)
tests/Unit/Cms/Controllers/PagesTest.php (2)
  • setUp (21-50)
  • tearDown (52-66)
tests/Unit/Cms/Controllers/HomeTest.php (2)
src/Cms/Controllers/Home.php (1)
  • Home (20-65)
src/Cms/Container/Container.php (1)
  • getInstance (251-254)
⏰ 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). (3)
  • GitHub Check: build-test (postgres)
  • GitHub Check: build-test (sqlite)
  • GitHub Check: build-test (mysql)
🔇 Additional comments (30)
tests/Unit/Cms/Services/Category/DeleterTest.php (1)

93-118: Well-structured test for event emission.

The test correctly verifies that when an event emitter is provided, a CategoryDeletedEvent is emitted upon deletion. The mock expectations properly validate the integration.

Consider also asserting the return value of delete() for completeness:

-		$deleter->delete( 1 );
+		$result = $deleter->delete( 1 );
+		$this->assertTrue( $result );
tests/Unit/Cms/Controllers/Member/DashboardTest.php (4)

1-16: LGTM! Clean test structure.

The imports, namespace, and class structure follow PHPUnit conventions and are consistent with other controller tests in the project.


41-54: LGTM! Proper test cleanup.

The teardown correctly clears Registry entries and removes the temporary version file. The error suppression on unlink is appropriate here as a defensive measure.


56-60: LGTM! Basic constructor test.

Simple smoke test verifying default construction works correctly.


62-67: LGTM! Dependency injection test.

Correctly tests the constructor with an injected Application dependency.

tests/Unit/Cms/Services/Media/CloudinaryUploaderTest.php (2)

17-75: LGTM! Well-structured test setup.

The setUp method properly configures test and integration scenarios, supporting both mocked tests with defaults and real Cloudinary integration tests via environment variables.


77-203: Excellent security test coverage!

The validation tests comprehensively cover SSRF protections, including all major private/reserved IP ranges and multiple attack vectors. The flexible assertion on Line 137 for localhost DNS resolution is a good practice given system variations.

tests/Unit/Cms/Services/Widget/CalendarWidgetTest.php (7)

117-142: Excellent coverage of widget metadata methods!

These tests properly verify the widget's public API for name, description, and attributes. The assertions confirm both the structure and expected values align with the implementation.


144-154: Good edge case coverage for empty event list.

This test correctly verifies that the widget handles the no-events scenario gracefully by displaying a user-friendly message while maintaining proper HTML structure.


156-221: Strong category filtering test coverage!

These tests thoroughly validate:

  • Error handling when a category slug doesn't exist (returns appropriate HTML comment)
  • Proper filtering by category ID via the repository
  • Limit enforcement when combined with category filtering

The mock setup correctly simulates the category lookup and event retrieval flow.


223-248: Comprehensive HTML structure validation.

This test ensures the complete rendering pipeline produces the expected HTML elements, CSS classes, URL patterns, and formatted dates. The assertions cover all critical components of the rendered output.


250-287: Excellent conditional rendering tests!

These tests verify that:

  • All-day events omit time information (no " at ", "AM", or "PM")
  • Events without locations don't render the location span

This confirms the widget correctly adapts its output based on event properties.


289-309: Critical XSS prevention test - well done!

This security-focused test verifies that malicious HTML/JavaScript in both the title and location fields is properly escaped. The assertions confirm that <script> and <img> tags are converted to safe HTML entities (&lt;script&gt;, &lt;img), preventing XSS attacks.


311-343: Thorough multi-event rendering validation.

This test verifies that multiple events are all rendered correctly and uses substr_count to confirm the exact number of list items matches the event count. Good attention to detail!

tests/Unit/Cms/Services/Widget/WidgetRendererTest.php (1)

180-207: Excellent dependency validation test coverage!

These three tests comprehensively validate the calendar widget's dependency requirements:

  1. No repositories: Confirms the error message when both repositories are missing
  2. Partial dependencies: Verifies that having only the event repository (without category repository) is insufficient
  3. Interface vs. concrete types: Tests the stricter requirement for concrete DatabaseEventRepository and DatabaseEventCategoryRepository implementations rather than just the interfaces

The expected error messages in the assertions correctly match the implementation logic shown in the relevant code snippets. This ensures the widget fails gracefully with clear, actionable error messages when dependencies are missing or incorrect.

tests/Unit/Cms/Controllers/Member/ProfileTest.php (3)

1-21: LGTM!

The imports and class structure are well-organized and follow PHPUnit conventions.


43-57: LGTM!

The tearDown method properly cleans up the Registry entries and removes the .version.json file. Using @unlink with error suppression is appropriate for test cleanup.


75-75: No changes needed. The Profile controller's constructor signature explicitly declares the first parameter as ?Application $app = null, which permits null values. The test is correctly passing null to this parameter.

tests/Unit/Cms/Controllers/PagesTest.php (6)

68-76: LGTM!

Constructor test correctly verifies dependency injection.


78-123: LGTM!

Comprehensive test that verifies the published page rendering flow, including view count increment and data passed to the renderer.


125-140: LGTM!

Correctly verifies NotFound exception behavior for nonexistent pages.


142-160: LGTM!

Correctly verifies NotFound exception behavior for unpublished pages.


162-200: LGTM!

Correctly tests fallback logic when meta title is empty.


202-242: LGTM!

Correctly tests fallback logic when meta description is empty.

tests/Unit/Cms/Controllers/CalendarTest.php (2)

103-146: LGTM!

Excellent test with proper date range validation using callbacks. The explicit date assertions ensure correct calendar month boundaries.


189-204: No action required. The Calendar and Pages controllers intentionally throw different exception types: Calendar throws RuntimeException (with 404 status code) for missing events/categories, while Pages throws NotFound for missing/unpublished pages. The tests correctly verify their respective controller behavior.

tests/Unit/Cms/Controllers/HomeTest.php (2)

65-79: LGTM!

Constructor tests correctly verify instantiation with and without the optional registration service dependency.


81-139: LGTM!

Comprehensive tests for index rendering with registration enabled and disabled states.

tests/Unit/Cms/Controllers/Admin/DashboardTest.php (2)

69-98: LGTM!

Good test of the index rendering with proper ViewContext fluent interface mocking. The @runInSeparateProcess annotation correctly isolates this test from global state.


56-67: LGTM!

Constructor tests correctly verify instantiation with and without the Application dependency.

Copy link
Contributor

@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

♻️ Duplicate comments (2)
tests/Unit/Cms/Controllers/Member/ProfileTest.php (1)

35-40: This fragile path pattern was already flagged in a previous review.

tests/Unit/Cms/Controllers/PagesTest.php (1)

45-49: Brittle file path assumption for version file.

This uses dirname(getcwd()) which assumes tests run from a specific directory. This concern was already raised in a previous review.

🧹 Nitpick comments (22)
src/Cms/Controllers/Member/Registration.php (1)

47-55: Consider removing nullable types for required parameters.

The constructor parameters are declared as nullable (?Type) but immediately validated as required. If these dependencies are always mandatory, removing the ? would make the contract clearer and allow PHP to enforce it at the type level rather than at runtime.

🔎 Optional refactor to use non-nullable parameters
 public function __construct(
 	?Application $app = null,
-	?IRegistrationService $registrationService = null,
-	?IEmailVerifier $emailVerifier = null,
+	IRegistrationService $registrationService,
+	IEmailVerifier $emailVerifier,
 	?SettingManager $settings = null,
 	?SessionManager $sessionManager = null,
-	?ResendVerificationThrottle $resendThrottle = null,
-	?IIpResolver $ipResolver = null
+	ResendVerificationThrottle $resendThrottle,
+	IIpResolver $ipResolver
 )

Then remove the corresponding null checks in the constructor body (lines 59-81).

Note: Keep nullable parameters for $app, $settings, and $sessionManager if the parent class handles them or if they support container resolution.

tests/Unit/Cms/Controllers/EventCategoriesControllerTest.php (3)

111-118: Redundant reflection-based SessionManager injection.

Since SessionManager is now injected via the constructor (line 86, 95), setting it again via reflection on the parent class's _sessionManager property appears redundant. The constructor should already populate this property via parent::__construct(). Consider removing this reflection code.

🔎 Proposed fix
-		// Mock session manager
-		$reflection = new \ReflectionClass( get_parent_class( EventCategories::class ) );
-		$sessionProperty = $reflection->getProperty( '_sessionManager' );
-		$sessionProperty->setAccessible( true );
-
-		$sessionManager = $this->createMock( SessionManager::class );
-		$sessionManager->method( 'getFlash' )->willReturn( null );
-		$sessionProperty->setValue( $controller, $sessionManager );
-

12-12: Unused import.

The Deleter class is imported but not used anywhere in this test file. Consider removing this unused import.


165-172: Same redundant reflection-based SessionManager injection.

Same issue as the testIndexReturnsAllCategories test—this reflection-based injection is redundant since SessionManager is already passed via constructor.

tests/Unit/Cms/Controllers/Member/DashboardTest.php (1)

32-38: Fragile version file path pattern.

The dirname(getcwd()) approach for creating .version.json assumes tests always run from the project root. This pattern appears across multiple test files in the suite. Consider using __DIR__ with a relative path to the project root, or adopting vfsStream for filesystem mocking to improve test reliability across different execution contexts.

tests/Unit/Cms/Controllers/UsersControllerTest.php (1)

141-152: Consider adding getFlash mock for consistency.

Unlike testIndexReturnsAllUsers, this test doesn't configure getFlash on the SessionManager mock. While the mocked view chain should prevent actual flash message access, adding the mock would maintain consistency across tests and prevent potential issues if test behavior changes.

tests/Unit/Cms/Controllers/Member/ProfileTest.php (1)

69-80: Consider adding negative tests for missing dependencies.

The AI summary mentions a testConstructorThrowsExceptionWithoutSettingManager test, but only testConstructorWithDependencies exists. Given that other controller tests in this PR (e.g., PagesTest, EventCategoriesControllerTest) include tests verifying exceptions when required dependencies are missing, consider adding similar negative tests here for consistency.

tests/Unit/Cms/Controllers/PostsControllerTest.php (2)

95-109: Redundant SessionManager mock injection.

The test creates a mockSessionManager, passes it to the Posts constructor (lines 95-108), but then immediately overrides it via reflection (lines 124-131) with a different mock. This pattern creates confusion about which mock is actually in use and makes the constructor injection pointless.

Consider either:

  1. Remove the reflection-based override and configure the constructor-injected mock with the necessary behavior (e.g., getFlash() return value), or
  2. Document why both approaches are needed if there's a specific technical reason.
🔎 Proposed cleanup

Configure the constructor-injected mock instead of overriding:

 $mockSettingManager = Registry::getInstance()->get( 'Settings' );
 $mockSessionManager = $this->createMock( SessionManager::class );
+$mockSessionManager->method( 'getFlash' )->willReturn( null );

 $controller = $this->getMockBuilder( Posts::class )
 	->setConstructorArgs([
 		null,
 		$postRepository,
 		$categoryRepository,
 		$tagRepository,
 		$creator,
 		$updater,
 		$deleter,
 		$mockSettingManager,
 		$mockSessionManager
 	])
 	->onlyMethods( ['view'] )
 	->getMock();

 // ... view builder setup ...

 $controller->method( 'view' )->willReturn( $viewBuilder );

-// Mock session manager via reflection
-$reflection = new \ReflectionClass( get_parent_class( Posts::class ) );
-$sessionProperty = $reflection->getProperty( '_sessionManager' );
-$sessionProperty->setAccessible( true );
-
-$sessionManager = $this->createMock( SessionManager::class );
-$sessionManager->method( 'getFlash' )->willReturn( null );
-$sessionProperty->setValue( $controller, $sessionManager );
-
 $request = $this->createMock( Request::class );

Also applies to: 124-131


168-182: Same redundancy pattern in additional tests.

The same redundant pattern (constructor injection + reflection override) appears in:

  • testIndexFiltersPostsByAuthorForNonAdmin (lines 168-182)
  • testCreateReturnsFormForAuthenticatedUser (lines 231-245)
  • testEditThrowsExceptionWhenUserUnauthorized (lines 288-301)

Apply the same cleanup approach across all affected tests for consistency.

Also applies to: 231-245, 288-301

tests/Unit/Cms/Controllers/MediaUploadTest.php (2)

68-73: Redundant mock injection with reflection override.

The test creates mocks for all dependencies (CloudinaryUploader, MediaValidator, SettingManager, SessionManager) and passes them to the Media constructor (lines 68-73), but then immediately overrides the validator via reflection (lines 110-119). This makes the constructor-injected mockMediaValidator unused.

Configure the constructor-injected mocks with the necessary behavior instead of overriding them via reflection.

🔎 Proposed cleanup
 $mockSettingManager = Registry::getInstance()->get( 'Settings' );
 $mockSessionManager = $this->createMock( \Neuron\Cms\Auth\SessionManager::class );
 $mockCloudinaryUploader = $this->createMock( CloudinaryUploader::class );
 $mockMediaValidator = $this->createMock( MediaValidator::class );
+$mockMediaValidator->method( 'validate' )->willReturn( false );
+$mockMediaValidator->method( 'getFirstError' )->willReturn( 'Invalid file type' );

 // Create Media controller
 $media = new Media( null, $mockCloudinaryUploader, $mockMediaValidator, $mockSettingManager, $mockSessionManager );

-// Create a mock validator that fails
-$validatorMock = $this->createMock( MediaValidator::class );
-$validatorMock->method( 'validate' )->willReturn( false );
-$validatorMock->method( 'getFirstError' )->willReturn( 'Invalid file type' );
-
-// Inject mock validator via reflection
-$reflection = new \ReflectionClass( $media );
-$validatorProperty = $reflection->getProperty( '_validator' );
-$validatorProperty->setAccessible( true );
-$validatorProperty->setValue( $media, $validatorMock );
-
 $request = $this->createMock( Request::class );

Also applies to: 102-120


140-145: Same redundancy pattern across all test methods.

The redundant pattern (constructor injection + reflection override) appears in all test methods:

  • testUploadFeaturedImageReturnsErrorWhenNoFileUploaded (lines 140-145)
  • testUploadFeaturedImageReturnsErrorWhenValidationFails (lines 174-180 + reflection override)
  • testUploadImageSuccessfulUpload (lines 219-225 + overrides for validator and uploader)
  • testUploadFeaturedImageSuccessfulUpload (lines 282-288 + overrides)
  • testUploadImageHandlesUploadException (lines 345-351 + overrides)
  • testUploadFeaturedImageHandlesUploadException (lines 398-404 + overrides)

Apply the same cleanup across all tests: configure constructor-injected mocks instead of using reflection.

Also applies to: 174-180, 219-225, 282-288, 345-351, 398-404

tests/Unit/Cms/Controllers/CategoriesControllerTest.php (2)

84-96: Redundant SessionManager mock injection.

Similar to the Posts controller tests, this test creates a mockSessionManager, passes it to the Categories constructor (lines 84-95), but then overrides it via reflection (lines 111-117). This creates the same confusion and redundancy issue.

Configure the constructor-injected mock with necessary behavior instead of using reflection.

Also applies to: 111-117


137-149: Same redundancy in additional tests.

The same pattern appears in:

  • testCreateReturnsForm (lines 137-149, 163-169)
  • testEditThrowsExceptionWhenCategoryNotFound (lines 191-202)

Apply consistent cleanup across all test methods.

Also applies to: 163-169, 191-202

tests/Unit/Cms/Controllers/EventsControllerTest.php (2)

89-102: Redundant SessionManager mock injection.

This test follows the same redundant pattern as the other controller tests: creating a mockSessionManager, passing it to the Events constructor (lines 89-101), then overriding it via reflection (lines 117-124).

For consistency with the recommended fixes in the other test files, configure the constructor-injected mock instead of using reflection.

Also applies to: 117-124


157-170: Same redundancy across all test methods.

The pattern repeats in:

  • testIndexFiltersEventsForNonAdmin (lines 157-170, 185-192)
  • testCreateReturnsFormForAuthenticatedUser (lines 216-229, 244-250)

Consider applying the same cleanup uniformly across all controller test files in this PR.

Also applies to: 185-192, 216-229, 244-250

tests/Unit/Cms/Controllers/PagesControllerTest.php (2)

115-122: Redundant session manager injection via reflection.

A SessionManager mock is already passed to the constructor on line 99, but here a new mock is created and injected via reflection, overwriting the constructor-injected value. This redundancy is unnecessary and could mask issues with the constructor wiring.

Consider removing this reflection-based injection since the dependency is now properly injected through the constructor.

🔎 Proposed fix
-		// Mock session manager via reflection
-		$reflection = new \ReflectionClass( get_parent_class( Pages::class ) );
-		$sessionProperty = $reflection->getProperty( '_sessionManager' );
-		$sessionProperty->setAccessible( true );
-
-		$sessionManager = $this->createMock( SessionManager::class );
-		$sessionManager->method( 'getFlash' )->willReturn( null );
-		$sessionProperty->setValue( $controller, $sessionManager );
+		// Configure the session manager mock that was passed to constructor
+		$mockSessionManager->method( 'getFlash' )->willReturn( null );

Note: You'll also need to configure $mockSessionManager on line 90 with the getFlash expectation before passing it to the constructor.


183-189: Same redundant pattern as in testIndexReturnsAllPagesForAdmin.

The reflection-based session manager injection duplicates what's already passed via the constructor. Consider removing this and configuring the mock before passing it to the constructor.

src/Cms/Controllers/Content.php (1)

242-256: Dead code: unreachable lazy-load fallback.

The comment states this is "for backward compatibility," but the constructor now throws InvalidArgumentException when $sessionManager is null (lines 90-93). This means $this->_sessionManager will always be set, making the conditional on lines 245-248 unreachable.

Consider removing this dead code to avoid confusion:

🔎 Proposed fix
 	protected function getSessionManager(): SessionManager
 	{
-		// Lazy-load if not injected (for backward compatibility)
-		if( !$this->_sessionManager )
-		{
-			$this->_sessionManager = new SessionManager();
-		}
-
 		if( !$this->_sessionManager->isStarted() )
 		{
 			$this->_sessionManager->start();
 		}

 		return $this->_sessionManager;
 	}
tests/Unit/Cms/Controllers/Admin/TagsTest.php (1)

63-69: LGTM with a suggestion for additional coverage.

Good test for verifying the parent's SettingManager validation. For completeness, consider adding tests for missing ITagRepository and SlugGenerator similar to PostsTest::testConstructorThrowsExceptionWithoutPostRepository, though this can be deferred.

tests/Unit/Cms/Controllers/CalendarTest.php (1)

37-41: Same brittle file path assumption as other test files.

This uses dirname(getcwd()) for the version file, which can break if tests run from different directories or in parallel. Consider extracting this setup logic to a shared test trait or base class that uses sys_get_temp_dir() with unique filenames per test.

🔎 Suggested approach

Create a shared test trait or base class:

trait VersionFileTestTrait
{
    private string $_tempVersionFile;
    
    protected function setUpVersionFile(): void
    {
        $this->_tempVersionFile = sys_get_temp_dir() . '/neuron_test_' . uniqid() . '/.version.json';
        mkdir(dirname($this->_tempVersionFile), 0777, true);
        file_put_contents($this->_tempVersionFile, json_encode([
            'major' => 1, 'minor' => 0, 'patch' => 0
        ]));
    }
    
    protected function tearDownVersionFile(): void
    {
        @unlink($this->_tempVersionFile);
        @rmdir(dirname($this->_tempVersionFile));
    }
}
tests/Unit/Cms/Controllers/Admin/DashboardTest.php (1)

16-18: Consider using typed properties consistently.

$_settingManager is typed as SettingManager, but $mockSettingManager and $mockSessionManager use the untyped $ syntax. Since $mockSettingManager is assigned from Registry::getInstance()->get('Settings') which returns the same SettingManager, consider typing it consistently.

🔎 Suggested improvement
 	private SettingManager $_settingManager;
-	private $mockSettingManager;
-	private $mockSessionManager;
+	private SettingManager $mockSettingManager;
+	private \Neuron\Cms\Auth\SessionManager $mockSessionManager;
src/Cms/Controllers/Blog.php (1)

143-143: Remove unnecessary null-safe operator.

The $_renderer property is now non-nullable (line 30) and the constructor throws if it's null (lines 69-71). The null-safe operator ?-> on line 143 is unnecessary and could mislead readers into thinking the property might be null.

🔎 Suggested fix
-		$renderedContent = $this->_renderer?->render( $content ) ?? (is_array($content) ? json_encode($content) : $content);
+		$renderedContent = $this->_renderer->render( $content );

If the fallback is intentional for when render() returns null, use:

-		$renderedContent = $this->_renderer?->render( $content ) ?? (is_array($content) ? json_encode($content) : $content);
+		$renderedContent = $this->_renderer->render( $content ) ?? (is_array($content) ? json_encode($content) : $content);
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f4c00dc and 102edd3.

📒 Files selected for processing (49)
  • src/Cms/Container/Container.php
  • src/Cms/Controllers/Admin/Categories.php
  • src/Cms/Controllers/Admin/Dashboard.php
  • src/Cms/Controllers/Admin/EventCategories.php
  • src/Cms/Controllers/Admin/Events.php
  • src/Cms/Controllers/Admin/Media.php
  • src/Cms/Controllers/Admin/Pages.php
  • src/Cms/Controllers/Admin/Posts.php
  • src/Cms/Controllers/Admin/Profile.php
  • src/Cms/Controllers/Admin/Tags.php
  • src/Cms/Controllers/Admin/Users.php
  • src/Cms/Controllers/Auth/Login.php
  • src/Cms/Controllers/Auth/PasswordReset.php
  • src/Cms/Controllers/Blog.php
  • src/Cms/Controllers/Calendar.php
  • src/Cms/Controllers/Content.php
  • src/Cms/Controllers/Home.php
  • src/Cms/Controllers/Member/Dashboard.php
  • src/Cms/Controllers/Member/Profile.php
  • src/Cms/Controllers/Member/Registration.php
  • src/Cms/Controllers/Pages.php
  • tests/Unit/Cms/BlogControllerTest.php
  • tests/Unit/Cms/ContentControllerTest.php
  • tests/Unit/Cms/Controllers/Admin/CategoriesTest.php
  • tests/Unit/Cms/Controllers/Admin/DashboardTest.php
  • tests/Unit/Cms/Controllers/Admin/EventCategoriesTest.php
  • tests/Unit/Cms/Controllers/Admin/EventsTest.php
  • tests/Unit/Cms/Controllers/Admin/PagesTest.php
  • tests/Unit/Cms/Controllers/Admin/PostsTest.php
  • tests/Unit/Cms/Controllers/Admin/ProfileTest.php
  • tests/Unit/Cms/Controllers/Admin/TagsTest.php
  • tests/Unit/Cms/Controllers/Admin/UsersTest.php
  • tests/Unit/Cms/Controllers/Auth/LoginTest.php
  • tests/Unit/Cms/Controllers/Auth/PasswordResetTest.php
  • tests/Unit/Cms/Controllers/CalendarTest.php
  • tests/Unit/Cms/Controllers/CategoriesControllerTest.php
  • tests/Unit/Cms/Controllers/EventCategoriesControllerTest.php
  • tests/Unit/Cms/Controllers/EventsControllerTest.php
  • tests/Unit/Cms/Controllers/HomeTest.php
  • tests/Unit/Cms/Controllers/MediaIndexTest.php
  • tests/Unit/Cms/Controllers/MediaUploadTest.php
  • tests/Unit/Cms/Controllers/Member/DashboardTest.php
  • tests/Unit/Cms/Controllers/Member/ProfileTest.php
  • tests/Unit/Cms/Controllers/Member/RegistrationTest.php
  • tests/Unit/Cms/Controllers/PagesControllerTest.php
  • tests/Unit/Cms/Controllers/PagesTest.php
  • tests/Unit/Cms/Controllers/PostsControllerTest.php
  • tests/Unit/Cms/Controllers/TagsControllerTest.php
  • tests/Unit/Cms/Controllers/UsersControllerTest.php
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/Unit/Cms/Controllers/Admin/ProfileTest.php
  • tests/Unit/Cms/Controllers/HomeTest.php
🧰 Additional context used
🧬 Code graph analysis (27)
tests/Unit/Cms/Controllers/PagesControllerTest.php (2)
src/Cms/Container/Container.php (1)
  • getInstance (268-271)
src/Cms/Auth/SessionManager.php (1)
  • SessionManager (13-216)
src/Cms/Controllers/Admin/Dashboard.php (2)
src/Cms/Controllers/Content.php (2)
  • Content (62-401)
  • __construct (77-119)
src/Cms/Controllers/Member/Dashboard.php (1)
  • __construct (32-39)
tests/Unit/Cms/Controllers/EventCategoriesControllerTest.php (1)
src/Cms/Auth/SessionManager.php (1)
  • SessionManager (13-216)
src/Cms/Controllers/Member/Dashboard.php (2)
src/Cms/Controllers/Content.php (2)
  • Content (62-401)
  • __construct (77-119)
src/Cms/Controllers/Admin/Dashboard.php (1)
  • __construct (30-37)
src/Cms/Controllers/Calendar.php (7)
src/Cms/Controllers/Admin/EventCategories.php (1)
  • __construct (43-71)
src/Cms/Controllers/Admin/Events.php (1)
  • __construct (47-82)
src/Cms/Controllers/Admin/Pages.php (1)
  • __construct (46-74)
src/Cms/Controllers/Admin/Users.php (1)
  • __construct (44-79)
src/Cms/Controllers/Blog.php (1)
  • __construct (43-78)
src/Cms/Controllers/Home.php (1)
  • __construct (33-49)
src/Cms/Controllers/Pages.php (1)
  • __construct (41-64)
src/Cms/Container/Container.php (4)
src/Cms/Services/Content/EditorJsRenderer.php (1)
  • EditorJsRenderer (12-370)
src/Cms/Services/Media/CloudinaryUploader.php (1)
  • CloudinaryUploader (17-371)
src/Cms/Services/Media/MediaValidator.php (1)
  • MediaValidator (15-193)
src/Cms/Auth/ResendVerificationThrottle.php (1)
  • ResendVerificationThrottle (19-195)
tests/Unit/Cms/Controllers/PostsControllerTest.php (2)
src/Cms/Container/Container.php (1)
  • getInstance (268-271)
src/Cms/Auth/SessionManager.php (1)
  • SessionManager (13-216)
tests/Unit/Cms/ContentControllerTest.php (2)
src/Cms/Auth/SessionManager.php (1)
  • SessionManager (13-216)
src/Cms/Controllers/Content.php (1)
  • Content (62-401)
tests/Unit/Cms/Controllers/Member/RegistrationTest.php (1)
src/Cms/Controllers/Member/Registration.php (1)
  • Registration (29-268)
src/Cms/Controllers/Admin/Media.php (3)
src/Cms/Controllers/Content.php (1)
  • Content (62-401)
src/Cms/Controllers/Admin/Dashboard.php (1)
  • __construct (30-37)
src/Cms/Controllers/Member/Dashboard.php (1)
  • __construct (32-39)
tests/Unit/Cms/Controllers/Admin/EventsTest.php (9)
src/Cms/Container/Container.php (1)
  • getInstance (268-271)
src/Cms/Auth/SessionManager.php (1)
  • SessionManager (13-216)
tests/Unit/Cms/Controllers/Admin/CategoriesTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (74-80)
tests/Unit/Cms/Controllers/Admin/EventCategoriesTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (66-72)
tests/Unit/Cms/Controllers/Admin/PagesTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (66-72)
tests/Unit/Cms/Controllers/Admin/PostsTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (76-82)
tests/Unit/Cms/Controllers/Admin/ProfileTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (89-95)
tests/Unit/Cms/Controllers/Admin/TagsTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (63-69)
tests/Unit/Cms/Controllers/Admin/UsersTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (69-75)
tests/Unit/Cms/Controllers/Admin/CategoriesTest.php (9)
src/Cms/Container/Container.php (1)
  • getInstance (268-271)
src/Cms/Auth/SessionManager.php (1)
  • SessionManager (13-216)
tests/Unit/Cms/Controllers/Admin/EventCategoriesTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (66-72)
tests/Unit/Cms/Controllers/Admin/EventsTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (69-75)
tests/Unit/Cms/Controllers/Admin/PagesTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (66-72)
tests/Unit/Cms/Controllers/Admin/PostsTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (76-82)
tests/Unit/Cms/Controllers/Admin/ProfileTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (89-95)
tests/Unit/Cms/Controllers/Admin/TagsTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (63-69)
tests/Unit/Cms/Controllers/Admin/UsersTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (69-75)
tests/Unit/Cms/Controllers/Admin/UsersTest.php (9)
src/Cms/Container/Container.php (1)
  • getInstance (268-271)
src/Cms/Auth/SessionManager.php (1)
  • SessionManager (13-216)
tests/Unit/Cms/Controllers/Admin/CategoriesTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (74-80)
tests/Unit/Cms/Controllers/Admin/EventCategoriesTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (66-72)
tests/Unit/Cms/Controllers/Admin/EventsTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (69-75)
tests/Unit/Cms/Controllers/Admin/PagesTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (66-72)
tests/Unit/Cms/Controllers/Admin/PostsTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (76-82)
tests/Unit/Cms/Controllers/Admin/ProfileTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (89-95)
tests/Unit/Cms/Controllers/Admin/TagsTest.php (1)
  • testConstructorThrowsExceptionWithoutSettingManager (63-69)
tests/Unit/Cms/Controllers/Admin/DashboardTest.php (2)
tests/Unit/Cms/BlogControllerTest.php (1)
  • setUp (32-102)
tests/Unit/Cms/Controllers/HomeTest.php (1)
  • setUp (18-47)
tests/Unit/Cms/Controllers/UsersControllerTest.php (2)
src/Cms/Container/Container.php (1)
  • getInstance (268-271)
src/Cms/Auth/SessionManager.php (1)
  • SessionManager (13-216)
tests/Unit/Cms/Controllers/TagsControllerTest.php (3)
src/Cms/Container/Container.php (1)
  • getInstance (268-271)
src/Cms/Auth/SessionManager.php (1)
  • SessionManager (13-216)
src/Cms/Services/SlugGenerator.php (1)
  • SlugGenerator (20-113)
src/Cms/Controllers/Admin/Posts.php (4)
src/Cms/Controllers/Admin/Dashboard.php (1)
  • __construct (30-37)
src/Cms/Controllers/Admin/Profile.php (1)
  • __construct (43-71)
src/Cms/Controllers/Admin/Tags.php (1)
  • __construct (40-61)
src/Cms/Controllers/Content.php (1)
  • __construct (77-119)
src/Cms/Controllers/Member/Profile.php (7)
src/Cms/Controllers/Content.php (2)
  • Content (62-401)
  • __construct (77-119)
src/Cms/Controllers/Admin/Pages.php (1)
  • __construct (46-74)
src/Cms/Controllers/Admin/Profile.php (1)
  • __construct (43-71)
src/Cms/Controllers/Auth/Login.php (1)
  • __construct (38-52)
src/Cms/Controllers/Auth/PasswordReset.php (1)
  • __construct (41-55)
src/Cms/Controllers/Home.php (1)
  • __construct (33-49)
src/Cms/Controllers/Member/Dashboard.php (1)
  • __construct (32-39)
src/Cms/Controllers/Admin/Categories.php (5)
src/Cms/Controllers/Content.php (2)
  • Content (62-401)
  • __construct (77-119)
src/Cms/Controllers/Admin/Events.php (1)
  • __construct (47-82)
src/Cms/Controllers/Admin/Pages.php (1)
  • __construct (46-74)
src/Cms/Controllers/Admin/Profile.php (1)
  • __construct (43-71)
src/Cms/Controllers/Admin/Tags.php (1)
  • __construct (40-61)
src/Cms/Controllers/Content.php (1)
src/Cms/Auth/SessionManager.php (2)
  • SessionManager (13-216)
  • get (134-138)
src/Cms/Controllers/Home.php (4)
src/Cms/Controllers/Admin/Dashboard.php (1)
  • __construct (30-37)
src/Cms/Controllers/Content.php (1)
  • __construct (77-119)
src/Cms/Controllers/Member/Dashboard.php (1)
  • __construct (32-39)
src/Cms/Services/Member/RegistrationService.php (1)
  • isRegistrationEnabled (61-64)
tests/Unit/Cms/Controllers/Auth/PasswordResetTest.php (2)
src/Cms/Controllers/Auth/PasswordReset.php (1)
  • PasswordReset (28-210)
src/Cms/Auth/SessionManager.php (1)
  • SessionManager (13-216)
tests/Unit/Cms/Controllers/CategoriesControllerTest.php (2)
src/Cms/Container/Container.php (1)
  • getInstance (268-271)
src/Cms/Auth/SessionManager.php (1)
  • SessionManager (13-216)
src/Cms/Controllers/Blog.php (5)
src/Cms/Auth/SessionManager.php (1)
  • SessionManager (13-216)
src/Cms/Controllers/Admin/Posts.php (1)
  • __construct (54-103)
src/Cms/Controllers/Auth/Login.php (1)
  • __construct (38-52)
src/Cms/Controllers/Content.php (1)
  • __construct (77-119)
src/Cms/Controllers/Home.php (1)
  • __construct (33-49)
src/Cms/Controllers/Admin/Tags.php (5)
src/Cms/Controllers/Content.php (1)
  • Content (62-401)
src/Cms/Controllers/Admin/Dashboard.php (1)
  • __construct (30-37)
src/Cms/Controllers/Admin/Media.php (1)
  • __construct (42-63)
src/Cms/Controllers/Auth/Login.php (1)
  • __construct (38-52)
src/Cms/Controllers/Member/Dashboard.php (1)
  • __construct (32-39)
tests/Unit/Cms/Controllers/Member/DashboardTest.php (2)
tests/Unit/Cms/Controllers/Admin/DashboardTest.php (6)
  • DashboardTest (14-105)
  • setUp (20-44)
  • tearDown (46-59)
  • testConstructor (61-65)
  • testConstructorWithApplication (67-72)
  • testIndexRendersView (78-104)
tests/Unit/Cms/Controllers/HomeTest.php (2)
  • setUp (18-47)
  • tearDown (49-63)
tests/Unit/Cms/Controllers/Auth/LoginTest.php (2)
src/Cms/Controllers/Auth/Login.php (1)
  • Login (25-184)
src/Cms/Auth/SessionManager.php (1)
  • SessionManager (13-216)
⏰ 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). (3)
  • GitHub Check: build-test (postgres)
  • GitHub Check: build-test (mysql)
  • GitHub Check: build-test (sqlite)
🔇 Additional comments (74)
src/Cms/Controllers/Member/Registration.php (1)

59-81: LGTM! Explicit dependency injection enforcement improves testability.

The fail-fast validation for all required dependencies is a solid improvement over container-based resolution. The exception messages are clear and consistent, making debugging straightforward.

tests/Unit/Cms/Controllers/Member/RegistrationTest.php (2)

127-141: LGTM! Comprehensive test coverage for explicit DI validation.

The renamed test accurately reflects the new behavior, and the exception message validation ensures clear error reporting.


143-189: Excellent test coverage for all required dependencies.

The three new tests comprehensively validate that each required dependency throws the expected exception when null. The tests are well-structured and follow a consistent pattern.

src/Cms/Controllers/Auth/PasswordReset.php (2)

5-5: LGTM: Use statements added correctly.

The new imports for SessionManager and SettingManager support the updated constructor signature.

Also applies to: 11-11


41-55: Explicit dependency validation enforced correctly.

The constructor now validates that IPasswordResetter is provided, throwing InvalidArgumentException if null. This aligns with the PR's goal of removing container-based fallbacks.

Note: All parameters are nullable in the signature, but $passwordResetter is effectively required at runtime. This pattern may support DI container compatibility but creates a slight API inconsistency where the type hint suggests optional but the implementation enforces required.

tests/Unit/Cms/Controllers/Auth/PasswordResetTest.php (3)

93-93: LGTM: Controller instantiation updated correctly.

The setUp() method properly instantiates the controller with all four dependencies, matching the new constructor signature.


103-110: LGTM: Constructor positive test added.

This test validates that the constructor works correctly when all dependencies are provided. Good coverage of the happy path.


112-121: LGTM: Exception validation test added correctly.

This test properly validates that InvalidArgumentException is thrown when IPasswordResetter is not provided. The expected exception message matches the production code exactly.

src/Cms/Container/Container.php (1)

239-249: LGTM on the new service bindings.

The new autowired bindings for EditorJsRenderer, CloudinaryUploader, MediaValidator, ResendVerificationThrottle, and IIpResolver are correctly structured. The autowire feature will handle constructor dependency resolution for services that depend on SettingManager or other already-registered services.

src/Cms/Controllers/Admin/Profile.php (1)

43-71: LGTM on the explicit dependency injection pattern.

The constructor correctly enforces required dependencies (IUserRepository, PasswordHasher, IUserUpdater) with clear exception messages. The SettingManager and SessionManager are passed to the parent constructor which handles their validation.

tests/Unit/Cms/Controllers/Member/DashboardTest.php (1)

56-107: LGTM on the test methods.

The constructor tests properly verify instantiation with and without an Application mock. The testIndexRendersView method correctly uses @runInSeparateProcess to avoid session conflicts and properly mocks the fluent ViewContext chain.

tests/Unit/Cms/Controllers/UsersControllerTest.php (1)

90-103: LGTM on the updated constructor arguments.

The test correctly injects SettingManager and SessionManager as the new constructor dependencies. The getFlash mock returning null ensures the controller's view rendering works without flash messages.

tests/Unit/Cms/Controllers/Admin/PagesTest.php (1)

49-72: LGTM on the updated constructor tests.

The testConstructorWithAllDependencies correctly passes the new SettingManager and SessionManager dependencies. The testConstructorThrowsExceptionWithoutSettingManager properly verifies that an InvalidArgumentException is thrown with the expected message when the dependency is missing. This pattern aligns well with the PR's DI migration approach.

tests/Unit/Cms/ContentControllerTest.php (2)

17-17: LGTM: Clean mock setup for SessionManager.

The addition of the mockSessionManager property and its initialization in setUp() properly supports the updated constructor signature for the Content controller.

Also applies to: 36-37


83-83: LGTM: Constructor calls consistently updated.

All Content controller instantiations have been correctly updated to pass the required three arguments (null, SettingManager, SessionManager), matching the new constructor signature.

Also applies to: 103-103, 131-131, 159-159, 186-186, 209-209

tests/Unit/Cms/Controllers/MediaIndexTest.php (2)

66-80: LGTM! Clean migration to constructor-based DI.

The test properly constructs all required dependencies (SettingManager, SessionManager, CloudinaryUploader, MediaValidator) and passes them via setConstructorArgs. The removal of reflection-based injection in favor of explicit constructor injection improves test clarity and aligns with modern DI best practices.


135-145: Good test coverage for cursor pagination.

The test properly validates that the cursor parameter is passed through to listResources with the expected options. Using $this->callback() to verify multiple conditions in the options array is a clean approach.

tests/Unit/Cms/Controllers/TagsControllerTest.php (1)

79-90: LGTM! Consistent DI pattern applied.

The test correctly creates mocks for all required dependencies (SlugGenerator, SettingManager, SessionManager) and passes them through the constructor. This aligns with the PR-wide shift to explicit dependency injection.

tests/Unit/Cms/Controllers/Auth/LoginTest.php (2)

62-69: LGTM! Good test coverage for successful construction.

The test validates that the Login controller can be constructed with all required dependencies.


71-80: LGTM! Good negative test for missing dependency.

The test properly validates that constructing Login without an IAuthenticationService throws InvalidArgumentException with the correct message. This enforces explicit dependency injection at the constructor level.

src/Cms/Controllers/Calendar.php (1)

37-58: LGTM! Consistent DI enforcement pattern applied.

The constructor properly enforces explicit dependency injection for IEventRepository and IEventCategoryRepository, throwing InvalidArgumentException with clear messages when dependencies are missing. The signature and validation pattern align with other controllers in the codebase (e.g., Admin/Events, Admin/Pages, Blog).

tests/Unit/Cms/Controllers/Admin/EventsTest.php (2)

51-67: LGTM! Comprehensive dependency injection test.

The test correctly constructs the Events controller with all required dependencies (Application, IEventRepository, IEventCategoryRepository, IEventCreator, IEventUpdater, SettingManager, SessionManager). This validates that the constructor accepts all expected dependencies.


69-75: LGTM! Good negative test for missing dependency.

The test validates that constructing Events without SettingManager throws InvalidArgumentException. This enforces explicit DI at the constructor level, consistent with other admin controller tests (Tags, Pages, Posts, Users, etc.).

src/Cms/Controllers/Member/Dashboard.php (2)

5-9: LGTM!

The new imports for SessionManager and SettingManager are properly added to support the explicit dependency injection pattern.


32-39: LGTM!

The constructor correctly accepts the new SettingManager and SessionManager dependencies and forwards them to the parent Content constructor. This aligns with the consistent DI pattern used across other controllers in the codebase (e.g., Admin\Dashboard, Auth\Login).

Minor note: The docblock @return void annotation on line 29 is unconventional for constructors but doesn't affect functionality.

tests/Unit/Cms/Controllers/PagesControllerTest.php (3)

89-100: LGTM!

The test correctly retrieves the mock SettingManager from the Registry and creates a mock SessionManager, passing both to the Pages controller constructor. This aligns with the new explicit DI pattern.


156-167: LGTM!

Test properly updated to pass the new dependencies to the Pages controller constructor.


208-220: LGTM!

This test correctly uses constructor injection without redundant reflection-based overwriting, serving as a good reference for refactoring the other tests.

src/Cms/Controllers/Content.php (2)

69-70: LGTM!

Good change from nullable to non-nullable types for the properties, enforcing the explicit DI contract.


85-96: LGTM!

The explicit null checks with descriptive exception messages provide clear error feedback when dependencies are missing. This enforces a fail-fast approach aligned with the pure DI pattern.

src/Cms/Controllers/Admin/Dashboard.php (2)

5-8: LGTM!

Proper imports added for the new dependencies.


30-37: LGTM!

The constructor correctly accepts and forwards SettingManager and SessionManager to the parent Content constructor, consistent with the DI pattern used across the codebase.

tests/Unit/Cms/Controllers/Admin/PostsTest.php (3)

56-74: LGTM!

The test correctly verifies that the Posts controller can be constructed when all dependencies are provided, including the new SettingManager and SessionManager dependencies.


76-82: LGTM!

Good negative test verifying that constructing Posts without a SettingManager throws the expected InvalidArgumentException. This validates the fail-fast behavior from the parent Content constructor.


84-103: LGTM!

Good test coverage for verifying that Posts throws when IPostRepository is missing, even when SettingManager and SessionManager are properly provided. This ensures the controller's own dependency validation works correctly.

src/Cms/Controllers/Admin/Tags.php (2)

5-11: LGTM!

Proper imports added for the new SessionManager and SettingManager dependencies.


40-61: LGTM!

The constructor properly:

  1. Accepts the new SettingManager and SessionManager dependencies
  2. Forwards them to the parent Content constructor (which validates them)
  3. Validates its own domain-specific dependencies (ITagRepository, SlugGenerator) with clear exception messages

This pattern is consistent with other controllers like Auth/Login and Admin/Media.

tests/Unit/Cms/Controllers/Admin/TagsTest.php (1)

47-61: LGTM!

The test correctly validates that Tags can be constructed with all required dependencies, including the new SettingManager and SessionManager.

tests/Unit/Cms/Controllers/Admin/EventCategoriesTest.php (2)

49-64: LGTM!

The test correctly verifies that EventCategories can be instantiated when all dependencies are explicitly provided, aligning with the new strict DI pattern.


66-72: LGTM!

The test properly validates that the constructor throws InvalidArgumentException when SettingManager is not injected, consistent with the pattern used in other controller tests (TagsTest, PagesTest, PostsTest, etc. as shown in the relevant code snippets).

src/Cms/Controllers/Pages.php (1)

41-64: LGTM!

The constructor properly enforces explicit dependency injection for IPageRepository and EditorJsRenderer while forwarding SettingManager and SessionManager to the parent. The fail-fast approach with descriptive exception messages aligns with the broader DI pattern established across the codebase.

src/Cms/Controllers/Admin/Events.php (1)

47-82: LGTM!

The constructor enforces explicit DI for all four required dependencies with clear, specific error messages. This aligns with the pattern established in other Admin controllers (Profile, Pages, Tags) as shown in the relevant code snippets.

src/Cms/Controllers/Home.php (1)

33-49: LGTM!

The constructor properly enforces explicit injection of IRegistrationService and forwards SettingManager and SessionManager to the parent Content constructor. The property type change to non-nullable on line 24 correctly reflects the fail-fast DI pattern.

src/Cms/Controllers/Auth/Login.php (1)

38-52: LGTM!

The constructor correctly enforces explicit injection of IAuthenticationService and properly forwards SettingManager and SessionManager to the parent. This aligns with the established DI pattern across the codebase.

src/Cms/Controllers/Admin/Categories.php (1)

42-70: LGTM!

The constructor properly enforces explicit DI for all three required dependencies (ICategoryRepository, ICategoryCreator, ICategoryUpdater) with clear error messages. The implementation is consistent with the pattern used in other Admin controllers.

tests/Unit/Cms/Controllers/Admin/CategoriesTest.php (3)

57-72: LGTM!

The test correctly verifies successful construction with all dependencies explicitly provided, matching the updated Categories constructor signature.


74-80: LGTM!

The test properly validates that missing SettingManager throws InvalidArgumentException, consistent with the pattern used in other admin controller tests (TagsTest, PagesTest, PostsTest, UsersTest, EventsTest, ProfileTest) as shown in the relevant code snippets.


82-98: LGTM!

Good test coverage for the ICategoryRepository null check. The test correctly provides the base dependencies (SettingManager and SessionManager) to ensure the parent constructor succeeds, then verifies that the repository-specific validation throws the expected exception.

src/Cms/Controllers/Admin/Pages.php (1)

46-74: LGTM! Constructor enforces explicit dependency injection.

The constructor properly validates all required dependencies and throws InvalidArgumentException when any are missing. This aligns with the project-wide shift from container-based resolution to explicit DI, as seen in the parent Content controller.

tests/Unit/Cms/Controllers/PagesTest.php (2)

68-128: Good test coverage for constructor and show method.

Tests properly verify constructor dependency wiring, published page rendering, and that incrementViewCount is called. The callback assertions for renderHtml effectively validate the data structure passed to the view.


130-169: Appropriate exception testing for edge cases.

Tests correctly verify that NotFound exceptions are thrown for both nonexistent and unpublished pages, matching the expected controller behavior.

src/Cms/Controllers/Admin/EventCategories.php (1)

43-71: LGTM! Consistent explicit DI pattern.

The constructor follows the same pattern as other admin controllers, properly validating and assigning all required dependencies.

tests/Unit/Cms/Controllers/Admin/UsersTest.php (2)

51-67: LGTM! Constructor test properly validates all dependencies.

The test correctly provides all required dependencies including the newly added SettingManager and SessionManager.


69-94: Good negative tests for missing dependencies.

Tests properly verify that InvalidArgumentException is thrown with appropriate messages when SettingManager or IUserRepository are missing. This pattern is consistent with other controller tests in the PR.

src/Cms/Controllers/Admin/Posts.php (1)

54-103: LGTM! Comprehensive dependency validation.

The constructor properly validates all six domain dependencies in addition to the settings and session manager. The pattern is consistent with other admin controllers, though Posts has the most dependencies to validate.

tests/Unit/Cms/BlogControllerTest.php (1)

261-278: Clean migration from reflection to constructor-based DI.

The helper method now properly constructs the Blog controller with all dependencies injected directly. The comment explaining why Model::setPdo() must be restored after Blog construction is helpful for maintainability.

tests/Unit/Cms/Controllers/CalendarTest.php (4)

58-68: LGTM! Constructor test validates dependency wiring.

The test properly verifies that the Calendar controller can be constructed with all required dependencies.


70-154: Comprehensive calendar rendering tests.

Good coverage of both current month and specific month/year scenarios. The callback assertions effectively validate that the correct data structure is passed to renderHtml.


200-241: Appropriate exception testing for events.

Tests correctly verify that RuntimeException is thrown for both nonexistent and unpublished events.


304-341: Good fallback behavior test.

Verifies that when an event's description is null, the event title is used as the description fallback.

src/Cms/Controllers/Admin/Media.php (3)

5-10: LGTM!

The new imports for SessionManager and SettingManager are correctly added to support the explicit DI pattern.


29-30: LGTM!

Converting properties from nullable to non-nullable correctly reflects the strict DI requirement enforced in the constructor.


42-63: LGTM!

The constructor properly:

  1. Forwards $settings and $sessionManager to the parent (which validates them)
  2. Validates that CloudinaryUploader and MediaValidator are injected
  3. Assigns dependencies to non-nullable properties

This is consistent with the DI pattern used across other controllers like Content.php and Admin/Dashboard.php.

src/Cms/Controllers/Admin/Users.php (2)

5-12: LGTM!

Import statements correctly added for SessionManager and SettingManager.


44-79: LGTM!

The constructor correctly:

  1. Forwards settings and session manager to the parent
  2. Validates all four service dependencies with clear error messages
  3. Assigns to non-nullable private properties

This is consistent with the DI pattern used in other admin controllers like Admin/Pages.php and Admin/Posts.php.

src/Cms/Controllers/Member/Profile.php (2)

5-10: LGTM!

Import statements for SessionManager and SettingManager correctly added.


39-67: LGTM!

The constructor implementation is consistent with Admin/Profile.php (as shown in relevant snippets at lines 42-70). Both controllers:

  1. Accept the same dependencies in the same order
  2. Forward settings/session to parent
  3. Validate and assign repository, hasher, and userUpdater identically

This maintains a clean, predictable pattern across the codebase.

tests/Unit/Cms/Controllers/Admin/DashboardTest.php (4)

20-44: LGTM!

The setup correctly:

  1. Creates in-memory settings with required site configuration
  2. Registers settings in the Registry (consistent with other tests like HomeTest.php and BlogControllerTest.php)
  3. Creates the version file for Content controller initialization
  4. Creates mock SessionManager for injection

This follows the established test patterns shown in the relevant snippets.


46-59: LGTM!

Proper cleanup of Registry entries and version file. Using @unlink to suppress errors if the file doesn't exist is appropriate for teardown.


61-72: LGTM!

Constructor tests correctly pass the new required dependencies (SettingManager and SessionManager) to the Dashboard constructor.


74-104: LGTM!

The view rendering test properly:

  1. Uses @runInSeparateProcess to avoid state pollution
  2. Mocks the view() method to isolate the test from actual view rendering
  3. Creates a fluent mock for ViewContext that returns itself for chained calls
  4. Verifies the expected output from index()

This is a good pattern for testing controller rendering without full integration.

src/Cms/Controllers/Blog.php (3)

5-15: LGTM!

Import statements for SessionManager and SettingManager correctly added.


26-30: LGTM!

Properties correctly changed to non-nullable, reflecting the strict DI requirements enforced in the constructor.


43-78: LGTM!

The constructor properly validates and assigns all dependencies. The pattern is consistent with Admin/Posts.php (shown in relevant snippets at lines 53-102).

Copy link
Contributor

@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

🧹 Nitpick comments (12)
tests/Unit/Cms/Controllers/Admin/DashboardTest.php (1)

16-19: Consider removing redundant property.

Both $_settingManager (line 16) and $mockSettingManager (line 18) hold references to the same SettingManager instance (see line 41 in setUp). The tests could use $this->_settingManager directly instead of $this->mockSettingManager, reducing redundancy.

🔎 Suggested simplification

Remove line 18 and update test methods to use $this->_settingManager instead of $this->mockSettingManager:

 	private SettingManager $_settingManager;
 	private string $_versionFilePath;
-	private $mockSettingManager;
 	private $mockSessionManager;

And in the test methods (lines 65, 72, 84):

-	$controller = new Dashboard( null, $this->mockSettingManager, $this->mockSessionManager );
+	$controller = new Dashboard( null, $this->_settingManager, $this->mockSessionManager );
tests/Unit/Cms/Controllers/HomeTest.php (6)

47-63: Consider a more robust Registry cleanup approach.

The tearDown method manually clears specific Registry keys. This approach is brittle—if new Registry entries are added in future tests or controller changes, they won't be automatically cleaned up, potentially causing test pollution.

Consider either:

  • Storing the original Registry state in setUp and restoring it in tearDown
  • Using a test-specific Registry instance if the framework supports it
  • Documenting which keys the test uses so maintainers know to update tearDown

Also, the isset() check on line 57 is unnecessary since $this->_versionFilePath is always initialized in setUp.

🔎 Proposed simplification for version file cleanup
 		// Clean up temp version file
-		if( isset( $this->_versionFilePath ) && file_exists( $this->_versionFilePath ) )
+		if( file_exists( $this->_versionFilePath ) )
 		{
 			unlink( $this->_versionFilePath );
 		}

85-149: Reduce code duplication by extracting a helper method.

The three index test methods share significant setup code: creating the same mocks, building a partial mock of the Home controller, and setting up renderHtml expectations. This duplication makes maintenance harder and increases the risk of inconsistencies.

Consider extracting a helper method like createHomeControllerMock(?bool $registrationEnabled) to centralize the common setup logic.

🔎 Example helper method to reduce duplication
private function createHomeControllerMock( ?bool $registrationEnabled ): Home
{
	$mockRegistrationService = $this->createMock( IRegistrationService::class );
	
	if( $registrationEnabled !== null )
	{
		$mockRegistrationService->method( 'isRegistrationEnabled' )
			->willReturn( $registrationEnabled );
	}
	
	$mockSessionManager = $this->createMock( \Neuron\Cms\Auth\SessionManager::class );
	
	return $this->getMockBuilder( Home::class )
		->setConstructorArgs( [ null, $mockRegistrationService, $this->_settingManager, $mockSessionManager ] )
		->onlyMethods( [ 'renderHtml' ] )
		->getMock();
}

Then each test can simply call $controller = $this->createHomeControllerMock( true ) or $controller = $this->createHomeControllerMock( false ).


99-112: Consider verifying the HTTP status code parameter.

All three index tests use $this->anything() for the first parameter to renderHtml, which means they don't verify the HTTP status code. According to the Home controller code (line 43 in src/Cms/Controllers/Home.php), the status should be HttpResponseStatus::OK.

Verifying the correct status code would make these tests more thorough.

🔎 Example change to verify HTTP status
 		$controller->expects( $this->once() )
 			->method( 'renderHtml' )
 			->with(
-				$this->anything(),
+				\Neuron\Mvc\HttpResponseStatus::OK,
 				$this->callback( function( $data ) {
 					// ...
 				} ),

Also applies to: 133-143, 165-179


151-151: Remove extra blank line.

Line 151 contains an unnecessary blank line between test methods.


152-183: Reconsider the necessity of this test method.

testIndexPassesCorrectDataToView appears redundant with testIndexWithRegistrationEnabled. Both tests:

  • Mock registration as enabled (line 155 vs line 88)
  • Verify the same data keys exist in the view data (lines 171-175 vs lines 104-108)
  • Use identical callback assertions to check data structure

The only difference is that line 182 doesn't assert the return value, whereas line 117 does—but that doesn't justify a separate test.

Consider either removing this test or differentiating it by testing a distinct scenario (e.g., verifying actual values of Title/Name/Description rather than just key existence).


85-183: Consider additional test scenarios for more comprehensive coverage.

The current tests cover the happy path well, but consider adding tests for:

  • Exception handling if isRegistrationEnabled() throws an exception
  • Verification that Title, Name, and Description contain the expected values from settings (currently only checking key existence)
  • Edge cases like empty or null setting values

These additions would make the test suite more robust, though they're not critical if the current coverage meets project standards.

src/Cms/Controllers/Content.php (1)

243-257: Remove unreachable backward-compatibility code.

The fallback logic on lines 246-249 that creates a new SessionManager if not injected is unreachable. The constructor (lines 90-93) now throws an InvalidArgumentException if $sessionManager is null, guaranteeing $this->_sessionManager is always set.

🔎 Proposed fix to remove unreachable code
 	protected function getSessionManager(): SessionManager
 	{
-		// Lazy-load if not injected (for backward compatibility)
-		if( !$this->_sessionManager )
-		{
-			$this->_sessionManager = new SessionManager();
-		}
-
 		if( !$this->_sessionManager->isStarted() )
 		{
 			$this->_sessionManager->start();
 		}
 
 		return $this->_sessionManager;
 	}
tests/Unit/Cms/ContentControllerTest.php (1)

18-18: Add type hint for consistency.

The $mockSessionManager property lacks a type hint, while $_settingManager (line 16) is properly typed. Add a type hint for consistency and better IDE support.

🔎 Proposed fix
-	private $mockSessionManager;
+	private \Neuron\Cms\Auth\SessionManager $mockSessionManager;
tests/Unit/Cms/Controllers/PagesTest.php (3)

103-106: Consider the trade-offs of partial mocking.

Several tests use onlyMethods() to partially mock the controller, replacing renderHtml() and getDescription() with stubs. While this isolates the show() method logic, it bypasses the actual rendering implementation and could mask issues in those methods.

Consider whether full integration tests (without partial mocking) would provide better coverage, or document why the partial approach is preferred here.

Also applies to: 192-195, 235-238


203-203: Consider stricter validation for title fallback.

Using str_contains($data['Title'], 'Test Page') is loose—it passes if 'Test Page' appears anywhere in the Title string. Consider using a more precise assertion like $data['Title'] === 'Test Page | Test Title' (or whatever the expected format is) to ensure the fallback logic constructs the title correctly.


130-147: Add negative assertion for incrementViewCount in failure cases.

Tests for nonexistent and unpublished pages throw NotFound exceptions but don't verify that incrementViewCount() is not called. Add $mockPageRepository->expects($this->never())->method('incrementViewCount') to ensure view counts don't increment for failed page lookups.

🔎 Proposed fix

For testShowThrowsNotFoundForNonexistentPage:

 $mockPageRepository = $this->createMock( IPageRepository::class );
 $mockPageRepository->method( 'findBySlug' )->with( 'nonexistent' )->willReturn( null );
+$mockPageRepository->expects( $this->never() )->method( 'incrementViewCount' );

For testShowThrowsNotFoundForUnpublishedPage:

 $mockPageRepository = $this->createMock( IPageRepository::class );
 $mockPageRepository->method( 'findBySlug' )->with( 'unpublished' )->willReturn( $mockPage );
+$mockPageRepository->expects( $this->never() )->method( 'incrementViewCount' );

Also applies to: 149-169

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 102edd3 and f84890b.

📒 Files selected for processing (17)
  • src/Cms/Container/CmsServiceProvider.php
  • src/Cms/Container/Container.php
  • src/Cms/Controllers/Content.php
  • src/Cms/Controllers/Member/Registration.php
  • src/Cms/Services/Security/ResendVerificationThrottle.php
  • tests/Unit/BootstrapTest.php
  • tests/Unit/Cms/Container/CmsServiceProviderTest.php
  • tests/Unit/Cms/ContentControllerTest.php
  • tests/Unit/Cms/Controllers/Admin/DashboardTest.php
  • tests/Unit/Cms/Controllers/Admin/ProfileTest.php
  • tests/Unit/Cms/Controllers/CalendarTest.php
  • tests/Unit/Cms/Controllers/HomeTest.php
  • tests/Unit/Cms/Controllers/Member/DashboardTest.php
  • tests/Unit/Cms/Controllers/Member/ProfileTest.php
  • tests/Unit/Cms/Controllers/Member/RegistrationTest.php
  • tests/Unit/Cms/Controllers/PagesTest.php
  • tests/Unit/Cms/Services/Security/ResendVerificationThrottleTest.php
✅ Files skipped from review due to trivial changes (1)
  • tests/Unit/Cms/Services/Security/ResendVerificationThrottleTest.php
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/Unit/Cms/Controllers/Member/ProfileTest.php
  • src/Cms/Container/Container.php
  • tests/Unit/Cms/Controllers/Member/DashboardTest.php
🧰 Additional context used
🧬 Code graph analysis (8)
tests/Unit/Cms/Controllers/HomeTest.php (1)
src/Cms/Controllers/Home.php (1)
  • Home (22-75)
src/Cms/Controllers/Member/Registration.php (1)
src/Cms/Services/Security/ResendVerificationThrottle.php (1)
  • ResendVerificationThrottle (19-195)
tests/Unit/Cms/ContentControllerTest.php (2)
src/Cms/Auth/SessionManager.php (2)
  • set (125-129)
  • SessionManager (13-216)
src/Cms/Controllers/Content.php (1)
  • Content (62-402)
tests/Unit/Cms/Controllers/Member/RegistrationTest.php (2)
src/Cms/Services/Security/ResendVerificationThrottle.php (1)
  • ResendVerificationThrottle (19-195)
src/Cms/Controllers/Member/Registration.php (1)
  • Registration (29-268)
src/Cms/Container/CmsServiceProvider.php (1)
src/Cms/Services/Security/ResendVerificationThrottle.php (1)
  • ResendVerificationThrottle (19-195)
src/Cms/Controllers/Content.php (1)
src/Cms/Auth/SessionManager.php (2)
  • SessionManager (13-216)
  • get (134-138)
tests/Unit/Cms/Container/CmsServiceProviderTest.php (1)
src/Cms/Services/Security/ResendVerificationThrottle.php (1)
  • ResendVerificationThrottle (19-195)
tests/Unit/Cms/Controllers/CalendarTest.php (1)
src/Cms/Container/Container.php (1)
  • getInstance (268-271)
⏰ 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). (4)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: build-test (postgres)
  • GitHub Check: build-test (mysql)
  • GitHub Check: build-test (sqlite)
🔇 Additional comments (35)
tests/Unit/BootstrapTest.php (1)

17-27: LGTM! Whitespace improvements enhance readability.

The blank lines added in the setUp method improve visual separation between logical blocks (property declarations, parent setup call, virtual filesystem setup, and environment handling), making the test initialization easier to scan.

tests/Unit/Cms/Controllers/CalendarTest.php (6)

22-58: Excellent test isolation and cleanup practices.

The setUp and tearDown methods demonstrate strong test isolation:

  • Unique temporary version file prevents inter-test conflicts
  • In-memory settings ensure no external dependencies
  • Thorough Registry cleanup prevents state leakage
  • Safe file deletion with existence check

60-70: Good constructor validation.

This test confirms the controller accepts its dependencies correctly. Using null for the Application parameter is appropriate for isolated unit testing.


72-156: Comprehensive coverage of calendar rendering scenarios.

These tests effectively validate:

  • Default current-month rendering with proper data structure
  • Specific month/year parameter handling with accurate date range calculations (lines 119-125)
  • Appropriate use of callbacks for complex DateTime assertions

The match expression for parameter mapping (lines 146-150) is clean and maintainable.


158-243: Thorough testing of event display and error handling.

Excellent coverage of:

  • Published event rendering with view count increment verification (lines 167-169)
  • Proper exception handling for both nonexistent and unpublished events
  • Appropriate use of PHPUnit's expectException API

245-304: Complete category listing validation.

These tests properly verify:

  • Category resolution by slug and event filtering by category ID with published status (line 252)
  • Required view data structure including category, events, and metadata
  • Exception handling for nonexistent categories

306-343: Valuable edge case coverage for missing descriptions.

This test validates the fallback behavior when an event's description is null, ensuring the view receives the event title as the Description field. Good defensive programming validation.

tests/Unit/Cms/Controllers/Admin/ProfileTest.php (3)

24-42: LGTM! Properly addresses the past review comment.

The setUp() method now correctly uses sys_get_temp_dir() with a unique identifier for the version file, storing the path in $_versionFilePath for cleanup. This resolves the previous concerns about unpredictable paths and race conditions.


44-61: LGTM! Thorough cleanup implementation.

The tearDown() method properly cleans up all Registry entries and safely removes the temporary version file with appropriate existence checks.


63-98: LGTM! Tests follow established patterns.

Both test methods are consistent with similar controller tests in the codebase (TagsTest, PagesTest, CategoriesTest). The docblock at lines 64-71 provides valuable context about the controller's testing limitations.

tests/Unit/Cms/Controllers/Admin/DashboardTest.php (4)

25-39: LGTM! Proper temp file setup.

The setUp() method correctly uses sys_get_temp_dir() with a unique identifier for the version file and configures Memory-based settings appropriately.


45-61: LGTM! Thorough cleanup implementation.

The tearDown() method properly cleans up all Registry entries and safely removes the temporary version file.


63-74: LGTM! Constructor tests validate dependency injection.

Both constructor tests appropriately verify that the Dashboard controller can be instantiated with the required dependencies.


76-106: LGTM! View rendering test with appropriate isolation.

The test correctly uses @runInSeparateProcess for isolation and properly mocks the fluent ViewContext interface to validate the rendering flow.

src/Cms/Controllers/Member/Registration.php (2)

7-7: LGTM: Import updated to reflect namespace reorganization.

The import path correctly reflects the namespace change of ResendVerificationThrottle from Neuron\Cms\Auth to Neuron\Cms\Services\Security.


59-82: LGTM: Explicit dependency validation enforces strict DI.

The fail-fast validation for all four required dependencies (IRegistrationService, IEmailVerifier, ResendVerificationThrottle, IIpResolver) is consistent with the PR's goal of removing container fallbacks and enforcing explicit injection.

src/Cms/Services/Security/ResendVerificationThrottle.php (1)

3-3: LGTM: Clean namespace reorganization.

The namespace move from Neuron\Cms\Auth to Neuron\Cms\Services\Security better reflects the class's purpose as a security service. The docblock is correctly updated to match.

Also applies to: 17-17

src/Cms/Container/CmsServiceProvider.php (3)

29-29: LGTM: Import updated for namespace change.

The import correctly reflects the namespace reorganization of ResendVerificationThrottle.


36-37: LGTM: IP resolver interface imports added.

The imports support the new IP resolver binding introduced in this PR.


115-120: LGTM: Auth service bindings configured correctly.

The singleton binding for ResendVerificationThrottle and interface binding for IIpResolverDefaultIpResolver are properly configured within the auth services registration.

tests/Unit/Cms/Container/CmsServiceProviderTest.php (1)

6-6: LGTM: Test import synchronized with namespace change.

The import path correctly updated to match the production code namespace reorganization.

src/Cms/Controllers/Content.php (3)

69-70: LGTM: Properties correctly declared as non-nullable.

The properties are now non-nullable, which aligns with the strict DI enforcement in the constructor.


85-96: LGTM: Explicit dependency validation enforces strict DI.

The fail-fast validation for required dependencies (SettingManager and SessionManager) is consistent with the PR's goal of removing service locator fallbacks.


98-102: LGTM: Direct property access is safe.

Since the constructor now guarantees non-null dependencies, direct access to $this->_settings is safe and correct.

Also applies to: 109-110

tests/Unit/Cms/ContentControllerTest.php (4)

24-31: LGTM! Good test isolation approach.

Creating a real temporary version file with a unique name ensures test isolation and avoids conflicts. The cleanup in tearDown (lines 61-64) properly removes the file.


44-46: Verify that no SessionManager behavior needs to be mocked.

The mock SessionManager is created without any configured expectations or return values. Ensure that the Content controller methods being tested don't invoke SessionManager methods that require stubbed behavior. Based on the test implementations, this appears intentional (testing dependency wiring rather than behavior), but worth confirming.


61-64: LGTM! Proper cleanup.

The tearDown correctly guards against missing property or file before unlinking, properly cleaning up the temporary file created in setUp.


74-74: LGTM! Consistent constructor updates.

All Content instantiations are consistently updated to pass the three required arguments (null Application, SettingManager, SessionManager), aligning with the new explicit dependency injection pattern.

Also applies to: 94-94, 122-122, 150-150, 177-177, 200-200

tests/Unit/Cms/Controllers/Member/RegistrationTest.php (5)

127-141: LGTM! Comprehensive constructor validation test.

The test properly verifies that the Registration controller throws InvalidArgumentException with the message "IRegistrationService must be injected" when the required dependency is null. This aligns with the PR's goal of enforcing explicit dependency injection.


143-157: LGTM! Consistent validation test for IEmailVerifier.

The test follows the same pattern as the previous constructor test, ensuring that IEmailVerifier is properly validated as a required dependency.


159-173: LGTM! Consistent validation test for ResendVerificationThrottle.

The test ensures that ResendVerificationThrottle is properly validated as a required dependency, maintaining consistency with the other constructor validation tests.


175-189: LGTM! Complete constructor validation coverage.

This test completes the comprehensive coverage of all four required dependencies for the Registration controller. Each dependency (IRegistrationService, IEmailVerifier, ResendVerificationThrottle, and IIpResolver) now has a dedicated test verifying that the constructor properly enforces explicit injection.


8-8: Namespace migration is complete and consistent. All references to ResendVerificationThrottle have been updated to the new Neuron\Cms\Services\Security namespace across the codebase, with no stale imports remaining in the old Neuron\Cms\Auth namespace. The class definition exists at the new location and is properly imported in all relevant files.

tests/Unit/Cms/Controllers/PagesTest.php (2)

75-75: No action needed - null is a valid first parameter for the Pages constructor.

The Pages constructor explicitly declares the first parameter as nullable (?Application $app = null) with a default value of null. Passing null is intentional and valid by design. The strict dependency injection requirements are for the $pageRepository and $renderer parameters, which throw InvalidArgumentException if null.


22-48: Remove unused version file setup code.

While the switch to sys_get_temp_dir() with a unique ID is a good improvement, the version file creation and configuration is unused—the Pages controller does not reference the version_file setting. Remove lines 26-33 (version file creation) and line 41 (the setting assignment) to simplify the test. The SettingManager instance is still necessary since it's passed to the parent constructor, but keep only the actually-used settings: site.name, site.title, site.description, and site.url.

Copy link
Contributor

@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: 0

🧹 Nitpick comments (1)
tests/Unit/Cms/Controllers/HomeTest.php (1)

148-179: Consider removing this duplicate test.

This test is nearly identical to testIndexWithRegistrationEnabled (lines 84-116)—both mock isRegistrationEnabled() to return true and verify the same data keys with the same assertions. The only difference is this test omits the return value assertion, making it strictly less comprehensive.

Either remove this test or differentiate it by testing a distinct scenario (e.g., verifying specific values like Title matching the configured setting).

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f84890b and 7b769ee.

📒 Files selected for processing (1)
  • tests/Unit/Cms/Controllers/HomeTest.php
🧰 Additional context used
🧬 Code graph analysis (1)
tests/Unit/Cms/Controllers/HomeTest.php (1)
src/Cms/Controllers/Home.php (1)
  • Home (22-75)
⏰ 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). (4)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: build-test (postgres)
  • GitHub Check: build-test (sqlite)
  • GitHub Check: build-test (mysql)
🔇 Additional comments (6)
tests/Unit/Cms/Controllers/HomeTest.php (6)

19-45: Well-structured test fixture setup.

Good use of temporary directory with unique suffix for test isolation, and proper in-memory settings configuration.


47-63: Good cleanup practice.

Proper cleanup of Registry state and temp files prevents test pollution.


65-73: Previous feedback addressed.

The test now correctly uses $this->_settingManager instance property instead of fetching from Registry.


75-82: LGTM!

The test correctly validates that the constructor enforces required dependencies.


84-116: LGTM!

Well-structured test using partial mock to verify renderHtml is called with expected data structure.


118-146: LGTM!

Good complementary test covering the registration-disabled scenario.

@ljonesfl ljonesfl closed this Dec 31, 2025
@ljonesfl ljonesfl deleted the feature/tests branch December 31, 2025 22:10
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