Skip to content

feat: enhance validation command options to allow skipping and specifying validators#16

Merged
konradmichalik merged 4 commits intomainfrom
skip-only
Jul 3, 2025
Merged

feat: enhance validation command options to allow skipping and specifying validators#16
konradmichalik merged 4 commits intomainfrom
skip-only

Conversation

@konradmichalik
Copy link
Copy Markdown
Contributor

@konradmichalik konradmichalik commented Jul 3, 2025

Summary by CodeRabbit

  • New Features

    • Added support for specifying multiple validators to include or skip when running translation validation commands, using new --only and --skip options.
  • Documentation

    • Updated usage instructions in the README to reflect new validator selection options.
  • Tests

    • Improved test coverage for validator selection, including handling of invalid or null validator inputs.
    • Added tests for class instantiation utility covering validation and error logging scenarios.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jul 3, 2025

Walkthrough

The changes update the validator selection mechanism in the translation validation command, replacing a single validator option with two new options: "only" and "skip" for specifying which validators to use or exclude. Supporting methods for validation and instantiation were refactored, and related documentation and tests were updated accordingly.

Changes

File(s) Change Summary
README.md Updated usage documentation to describe new --only and --skip options for validator selection.
src/Command/ValidateTranslationCommand.php Refactored validator option handling to support "only" and "skip" options; added new methods for resolving validators; removed old validation method.
src/Utility/ClassUtility.php Added instantiate static method for class validation and instantiation with error logging.
tests/src/Command/ValidateTranslationCommandTest.php Modified tests to expect exceptions for invalid validators and updated option usage in tests.
tests/src/Utility/ClassUtilityTest.php Added tests for the new instantiate method covering null, non-existent, invalid, and valid classes.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Command as ValidateTranslationCommand
    participant Utility as ClassUtility
    participant Logger

    User->>Command: Run validate-translations with --only/--skip
    Command->>Command: resolveValidators(input)
    Command->>Utility: validateClassInput(interface, type, classNames)
    Utility-->>Command: List of valid validator class names
    loop For each validator
        Command->>Utility: instantiate(interface, logger, type, className)
        Utility-->>Command: Validator instance or null
    end
    Command->>Logger: Log errors if invalid classes
    Command->>Command: Run validation with selected validators
Loading

Possibly related PRs

Poem

Validators now can skip or run,
With options bright as morning sun.
"Only" and "skip"—the choices grow,
Refactored code now in full flow.
Tests and docs keep pace,
🐇 hops with grace—
Translation checks in every place!


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abae998 and 535eae9.

📒 Files selected for processing (2)
  • src/Command/ValidateTranslationCommand.php (4 hunks)
  • tests/src/Utility/ClassUtilityTest.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/src/Utility/ClassUtilityTest.php
  • src/Command/ValidateTranslationCommand.php
✨ 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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/Command/ValidateTranslationCommand.php (1)

197-216: Consider optimizing the validation approach.

The method instantiates classes only for validation purposes but discards the instances. Since ClassUtility::validateClass can validate without instantiation, consider using it directly to avoid unnecessary object creation.

 private function validateClassInput(string $interface, string $type, ?string $className = null): array
 {
     if (null === $className) {
         return [];
     }
     $classes = [];
 
     if (str_contains($className, ',')) {
         $classNames = explode(',', $className);
         foreach ($classNames as $name) {
-            ClassUtility::instantiate($interface, $this->logger, $type, $name);
+            $name = trim($name);
+            if (ClassUtility::validateClass($interface, $this->logger, $name)) {
+                $classes[] = $name;
+            }
-            $classes[] = $name;
         }
     } else {
-        ClassUtility::instantiate($interface, $this->logger, $type, $className);
+        if (ClassUtility::validateClass($interface, $this->logger, $className)) {
+            $classes[] = $className;
+        }
-        $classes[] = $className;
     }
 
     return $classes;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 23059db and 5c18484.

📒 Files selected for processing (4)
  • README.md (1 hunks)
  • src/Command/ValidateTranslationCommand.php (4 hunks)
  • src/Utility/ClassUtility.php (1 hunks)
  • tests/src/Command/ValidateTranslationCommandTest.php (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/Command/ValidateTranslationCommand.php (1)
src/Utility/ClassUtility.php (1)
  • instantiate (11-26)
🔇 Additional comments (7)
src/Utility/ClassUtility.php (1)

11-26: LGTM! Well-structured utility method.

The implementation properly handles null inputs, validates classes against interfaces, and provides clear error messages. The parameterless constructor assumption is reasonable for the intended use case.

README.md (1)

42-42: Documentation accurately reflects the new validator options.

The update correctly shows the new -s|--skip and -o|--only options that replace the previous --validator option.

tests/src/Command/ValidateTranslationCommandTest.php (2)

74-85: Test correctly updated to handle the new validation approach.

The test now properly captures the exception thrown when attempting to instantiate an invalid validator class via the --only option.


245-245: Option correctly updated to use the new --skip flag.

src/Command/ValidateTranslationCommand.php (3)

78-88: Well-designed validator selection options.

The new --only and --skip options provide flexible control over validator selection, improving upon the single validator limitation.


108-113: Clean refactoring using the utility method.

Good use of the new ClassUtility::instantiate method for file detector instantiation.


170-192: Logical and clear validator resolution.

The precedence handling (--only takes priority over --skip) is well-implemented.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c18484 and abae998.

📒 Files selected for processing (1)
  • tests/src/Utility/ClassUtilityTest.php (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: CGL
tests/src/Utility/ClassUtilityTest.php

[error] 69-69: Rector migration failed with exit code 2 during dry-run. Suggested changes involve method signature update for 'error' method callback to 'function (string|\Stringable $message)'.

🔇 Additional comments (2)
tests/src/Utility/ClassUtilityTest.php (2)

60-66: LGTM!

The test correctly verifies that instantiate returns null when passed a null class name without logging errors.


95-103: LGTM!

The test correctly verifies that instantiate returns a proper instance when passed a valid class implementing the required interface.

Comment on lines +82 to +93
public function testInstantiateWithClassNotImplementingInterface(): void
{
$loggedMessages = [];
$logger = $this->createMock(LoggerInterface::class);
$logger->method('error')->willReturnCallback(function ($message) use (&$loggedMessages) {
$loggedMessages[] = $message;
});

$this->assertNull(ClassUtility::instantiate(DummyInterface::class, $logger, 'test', InvalidClass::class));

$this->assertStringContainsString('The test class "MoveElevator\ComposerTranslationValidator\Tests\Utility\InvalidClass" must implement MoveElevator\ComposerTranslationValidator\Tests\Utility\DummyInterface', $loggedMessages[1]);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix unsafe array access and method signature issues.

This test has the same critical issues as the previous method:

  1. Pipeline failure: The callback signature needs updating.
  2. Unsafe array access: Line 92 accesses array index 1 without verification.

Apply this diff to fix the issues:

-        $logger->method('error')->willReturnCallback(function ($message) use (&$loggedMessages) {
+        $logger->method('error')->willReturnCallback(function (string|\Stringable $message) use (&$loggedMessages) {
             $loggedMessages[] = $message;
         });
 
         $this->assertNull(ClassUtility::instantiate(DummyInterface::class, $logger, 'test', InvalidClass::class));
 
+        $this->assertGreaterThanOrEqual(2, count($loggedMessages), 'Expected at least 2 error messages');
         $this->assertStringContainsString('The test class "MoveElevator\ComposerTranslationValidator\Tests\Utility\InvalidClass" must implement MoveElevator\ComposerTranslationValidator\Tests\Utility\DummyInterface', $loggedMessages[1]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function testInstantiateWithClassNotImplementingInterface(): void
{
$loggedMessages = [];
$logger = $this->createMock(LoggerInterface::class);
$logger->method('error')->willReturnCallback(function ($message) use (&$loggedMessages) {
$loggedMessages[] = $message;
});
$this->assertNull(ClassUtility::instantiate(DummyInterface::class, $logger, 'test', InvalidClass::class));
$this->assertStringContainsString('The test class "MoveElevator\ComposerTranslationValidator\Tests\Utility\InvalidClass" must implement MoveElevator\ComposerTranslationValidator\Tests\Utility\DummyInterface', $loggedMessages[1]);
}
public function testInstantiateWithClassNotImplementingInterface(): void
{
$loggedMessages = [];
$logger = $this->createMock(LoggerInterface::class);
$logger->method('error')->willReturnCallback(function (string|\Stringable $message) use (&$loggedMessages) {
$loggedMessages[] = $message;
});
$this->assertNull(ClassUtility::instantiate(DummyInterface::class, $logger, 'test', InvalidClass::class));
$this->assertGreaterThanOrEqual(2, count($loggedMessages), 'Expected at least 2 error messages');
$this->assertStringContainsString(
'The test class "MoveElevator\ComposerTranslationValidator\Tests\Utility\InvalidClass" must implement MoveElevator\ComposerTranslationValidator\Tests\Utility\DummyInterface',
$loggedMessages[1]
);
}
🤖 Prompt for AI Agents
In tests/src/Utility/ClassUtilityTest.php around lines 82 to 93, the test method
has an unsafe array access on $loggedMessages[1] without checking if that index
exists, and the callback signature for the logger mock's error method is
incorrect. To fix this, update the callback to match the expected signature of
the error method, and add a check to ensure the $loggedMessages array has the
required index before accessing it, or adjust the assertion to safely verify the
logged message.

Comment on lines +68 to +80
public function testInstantiateWithNonExistentClass(): void
{
$loggedMessages = [];
$logger = $this->createMock(LoggerInterface::class);
$logger->method('error')->willReturnCallback(function ($message) use (&$loggedMessages) {
$loggedMessages[] = $message;
});

ClassUtility::instantiate(DummyInterface::class, $logger, 'test', 'NonExistentClass');

$this->assertStringContainsString('The class "NonExistentClass" does not exist.', $loggedMessages[0]);
$this->assertStringContainsString('The test class "NonExistentClass" must implement MoveElevator\ComposerTranslationValidator\Tests\Utility\DummyInterface', $loggedMessages[1]);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix unsafe array access and method signature issues.

The test has several critical issues:

  1. Pipeline failure: The callback signature needs to match function (string|\Stringable $message) based on the Rector migration error.
  2. Unsafe array access: Lines 78-79 access array indices without verifying the array has those elements.

Apply this diff to fix the issues:

-        $logger->method('error')->willReturnCallback(function ($message) use (&$loggedMessages) {
+        $logger->method('error')->willReturnCallback(function (string|\Stringable $message) use (&$loggedMessages) {
             $loggedMessages[] = $message;
         });
 
         ClassUtility::instantiate(DummyInterface::class, $logger, 'test', 'NonExistentClass');
 
-        $this->assertStringContainsString('The class "NonExistentClass" does not exist.', $loggedMessages[0]);
-        $this->assertStringContainsString('The test class "NonExistentClass" must implement MoveElevator\ComposerTranslationValidator\Tests\Utility\DummyInterface', $loggedMessages[1]);
+        $this->assertCount(2, $loggedMessages, 'Expected exactly 2 error messages');
+        $this->assertStringContainsString('The class "NonExistentClass" does not exist.', $loggedMessages[0]);
+        $this->assertStringContainsString('The test class "NonExistentClass" must implement MoveElevator\ComposerTranslationValidator\Tests\Utility\DummyInterface', $loggedMessages[1]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function testInstantiateWithNonExistentClass(): void
{
$loggedMessages = [];
$logger = $this->createMock(LoggerInterface::class);
$logger->method('error')->willReturnCallback(function ($message) use (&$loggedMessages) {
$loggedMessages[] = $message;
});
ClassUtility::instantiate(DummyInterface::class, $logger, 'test', 'NonExistentClass');
$this->assertStringContainsString('The class "NonExistentClass" does not exist.', $loggedMessages[0]);
$this->assertStringContainsString('The test class "NonExistentClass" must implement MoveElevator\ComposerTranslationValidator\Tests\Utility\DummyInterface', $loggedMessages[1]);
}
public function testInstantiateWithNonExistentClass(): void
{
$loggedMessages = [];
$logger = $this->createMock(LoggerInterface::class);
$logger->method('error')->willReturnCallback(function (string|\Stringable $message) use (&$loggedMessages) {
$loggedMessages[] = $message;
});
ClassUtility::instantiate(DummyInterface::class, $logger, 'test', 'NonExistentClass');
$this->assertCount(2, $loggedMessages, 'Expected exactly 2 error messages');
$this->assertStringContainsString(
'The class "NonExistentClass" does not exist.',
$loggedMessages[0]
);
$this->assertStringContainsString(
'The test class "NonExistentClass" must implement MoveElevator\ComposerTranslationValidator\Tests\Utility\DummyInterface',
$loggedMessages[1]
);
}
🧰 Tools
🪛 GitHub Actions: CGL

[error] 69-69: Rector migration failed with exit code 2 during dry-run. Suggested changes involve method signature update for 'error' method callback to 'function (string|\Stringable $message)'.

🤖 Prompt for AI Agents
In tests/src/Utility/ClassUtilityTest.php around lines 68 to 80, the error
callback's parameter type should be updated to accept string or Stringable to
match the expected signature, and before accessing $loggedMessages[0] and
$loggedMessages[1], add checks to ensure these indices exist to prevent unsafe
array access errors. Modify the callback function parameter accordingly and add
assertions or conditional checks to verify the array elements exist before
asserting their contents.

@konradmichalik konradmichalik merged commit fd81de4 into main Jul 3, 2025
24 checks passed
@konradmichalik konradmichalik deleted the skip-only branch July 28, 2025 17:14
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.

1 participant