Skip to content

Release v0.1.3 — Phase 03: Converter Domain Core#59

Merged
menvil merged 37 commits into
mainfrom
release/v0.1.3-phase03-converter-domain-core
May 28, 2026
Merged

Release v0.1.3 — Phase 03: Converter Domain Core#59
menvil merged 37 commits into
mainfrom
release/v0.1.3-phase03-converter-domain-core

Conversation

@menvil

@menvil menvil commented May 28, 2026

Copy link
Copy Markdown
Owner

Summary

Phase 03 — Converter Domain Core (CONV-031 → CONV-045).

Introduces:

  • App\Enums\FileFormat with normalization (jpeg→jpg, uppercase, leading dot) and UnsupportedFormatException
  • Converter contract + ConverterTarget DTO
  • ConverterRegistry with all/forSource/find/targetsFor
  • OptionsSchemaField DTO, OptionsSchemaValidator, OptionsValidator
  • Test-only fake converters under tests/Fakes/Converters

No real conversion drivers, file upload, billing, or API in this phase.

Test plan

  • composer test — 113 passed
  • composer lint — passed
  • npm run build — passed
  • php artisan migrate:fresh — clean

🤖 Generated with Claude Code


Summary by cubic

Builds the Converter domain core: format normalization, converter contract/registry, DTOs, and options/schema validation (CONV-031 → CONV-046). Also tightens color option validation and restricts CodeRabbit auto-review to PRs labeled "release" (INFRA-002).

  • New Features

    • App\Enums\FileFormat normalizes inputs (jpeg→jpg, case, leading dot) and throws UnsupportedFormatException.
    • Converter interface and ConverterRegistry (all, forSource, find, targetsFor) with ConverterTarget DTO.
    • OptionsSchemaField DTO plus OptionsSchemaValidator and OptionsValidator for supported types (select, segmented, toggle, color, number, range), defaults, required fields, allowed values, and boolean/number/color normalization with clear errors.
    • Test-only converters: PNG→JPG, PNG→PDF, JPG→WEBP.
  • Bug Fixes

    • OptionsSchemaField::toArray() omits default when not set to avoid treating null as a default.
    • OptionsValidator color check now only accepts 3/4/6/8-digit hex codes.
    • .coderabbit.yaml: enable auto-review with label filter so reviews run only on PRs labeled "release".

Written for commit 7d10b59. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added support for file format conversion between PNG, JPG, WEBP, and PDF formats.
    • Implemented converter registry system to manage multiple conversion pathways.
    • Added comprehensive validation for conversion options and format inputs.
  • Chores

    • Updated CodeRabbit auto-review configuration to enable reviews on labeled pull requests.

Review Change Stack

menvil and others added 30 commits May 28, 2026 23:48
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…m-skeleton

CONV-031: Create Format enum skeleton
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…lization-rules

CONV-032: Test format normalization rules
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…normalization

CONV-033: Implement format normalization
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…contract

CONV-034: Create Converter contract
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…target-dto

CONV-035: Create ConverterTarget DTO
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…rter-implementations

CONV-036: Create fake converter implementations
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…registry-skeleton

CONV-037: Create ConverterRegistry skeleton
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ts-converters-by-source

CONV-038: Test registry lists converters by source
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…y-lookup-methods

CONV-039: Implement registry lookup methods
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…hema-field-structure

CONV-040: Create options schema field structure
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ma-validation

CONV-041: Test options schema validation
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…-schema-validator

CONV-042: Implement options schema validator
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…lidator-skeleton

CONV-043: Create OptionsValidator skeleton
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ults-and-invalid-values

CONV-044: Test options defaults and invalid values
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…-validation

CONV-045: Implement options validation
@github-actions github-actions Bot added the release Triggers AI code review (CodeRabbit, Cubic) label May 28, 2026
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR introduces a converter system foundation: a FileFormat enum with normalization, a Converter interface contract, DTOs for output/schema metadata, exception-driven validation for both schema and option inputs, a registry for discovering converters, and comprehensive test coverage via fakes and unit tests. CodeRabbit auto-review is also enabled for release-labeled PRs.

Changes

Converter System and Options Validation

Layer / File(s) Summary
CodeRabbit auto-review configuration
.coderabbit.yaml
Enables auto-review for PRs carrying the release label.
Converter contract and output structures
app/Support/Converters/Contracts/Converter.php, app/Support/Converters/DTO/ConverterTarget.php, app/Support/Converters/DTO/OptionsSchemaField.php, tests/Unit/Converters/ConverterContractTest.php, tests/Unit/Converters/ConverterTargetTest.php, tests/Unit/Converters/OptionsSchemaFieldTest.php
Converter interface defines identification, format metadata, presentation fields, and options validation. ConverterTarget DTO serializes available conversion targets with format, label, description, and recommendation flag. OptionsSchemaField DTO encapsulates schema field metadata with key, type, defaults, choices, and help text.
File format representation and normalization
app/Enums/FileFormat.php, app/Support/Converters/Exceptions/UnsupportedFormatException.php, tests/Unit/Converters/FileFormatTest.php
FileFormat enum defines supported formats (png, jpg, webp, pdf). normalize() handles case insensitivity, leading-dot stripping, and maps jpeg to jpg. Unsupported formats throw UnsupportedFormatException.
Options validation framework: exceptions and validators
app/Support/Converters/Exceptions/InvalidConverterOptionsException.php, app/Support/Converters/Exceptions/InvalidOptionsSchemaException.php, app/Support/Converters/OptionsSchemaValidator.php, app/Support/Converters/OptionsValidator.php, tests/Unit/Converters/OptionsSchemaValidatorTest.php, tests/Unit/Converters/OptionsValidatorTest.php
InvalidConverterOptionsException and InvalidOptionsSchemaException provide factory methods for scenario-specific error messages. OptionsSchemaValidator enforces schema structure: unique non-empty keys, supported types, non-empty labels, and required options arrays for choice-type fields. OptionsValidator normalizes user options against a schema, applying defaults, coercing types (toggle to boolean, number/range to numeric, color to hex pattern), validating allowed values, and enforcing required fields.
Converter registry and discovery
app/Support/Converters/ConverterRegistry.php, tests/Unit/Converters/ConverterRegistryTest.php
ConverterRegistry stores and queries converters via constructor-injected array. all() returns all registered converters; forSource() filters by source format (normalized); find() locates a converter for a source/target pair or returns null; targetsFor() builds ConverterTarget DTOs for available conversions from a given source.
Test doubles and comprehensive test coverage
tests/Fakes/Converters/FakePngToJpgConverter.php, tests/Fakes/Converters/FakePngToPdfConverter.php, tests/Fakes/Converters/FakeJpgToWebpConverter.php, tests/Unit/Converters/FakeConverterTest.php
Three fake converter implementations provide fixed metadata for PNG→JPG, PNG→PDF, and JPG→WEBP conversions with pass-through option handling. Unit tests verify contract compliance and expected format/key values.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Registry
    participant OptionsValidator
    participant Converter
    User->>Registry: targetsFor(format)
    activate Registry
    Registry->>Registry: forSource(format)
    Registry-->>User: ConverterTarget[]
    deactivate Registry
    User->>Registry: find(source, target)
    activate Registry
    Registry-->>User: Converter
    deactivate Registry
    User->>Converter: validateOptions(options)
    activate Converter
    Converter->>OptionsValidator: validate(schema, options)
    OptionsValidator-->>Converter: normalized_options
    Converter-->>User: options_result
    deactivate Converter
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • menvil/FileConverter#31: Both PRs update .coderabbit.yaml to enable auto-review functionality gated by the release label.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.90% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: implementing the Converter domain core (Phase 03) for release v0.1.3, which aligns with the substantial additions of FileFormat enum, Converter interface, ConverterRegistry, and validation classes throughout the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch release/v0.1.3-phase03-converter-domain-core

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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.

menvil and others added 2 commits May 29, 2026 00:28
Previously enabled=false with labels listed, which disabled all reviews
including release/* → main PRs. Switched to enabled=true so the labels
filter actually narrows auto-review to PRs carrying the "release" label.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Previously enabled=false with labels listed, which disabled all reviews
including release/* → main PRs. Switched to enabled=true so the labels
filter actually narrows auto-review to PRs carrying the "release" label.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
tests/Unit/Converters/OptionsSchemaFieldTest.php (1)

26-44: ⚡ Quick win

Add a regression case for “no default provided”.

Please add a test that omits default and asserts toArray() does not emit a default key. That locks the intended semantics and prevents validator behavior regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Unit/Converters/OptionsSchemaFieldTest.php` around lines 26 - 44, Add a
regression test that constructs an OptionsSchemaField without passing the
default argument and asserts that toArray() does not include a 'default' key;
specifically, in OptionsSchemaFieldTest create a new case similar to the
existing "exposes toArray representation" but omit the default parameter when
instantiating OptionsSchemaField and assert the returned array equals the
expected array that lacks any 'default' entry (use OptionsSchemaField::toArray()
as the inspected method and ensure 'options', 'required', and 'help' behave as
before).
tests/Unit/Converters/ConverterContractTest.php (1)

10-20: ⚡ Quick win

Strengthen contract test with signature assertions.

This currently checks only method names. Add assertions for parameter counts/types and return types so contract drift is caught early.

Example addition
     foreach ([
         'key',
         'sourceFormat',
         'targetFormat',
         'label',
         'description',
         'optionsSchema',
         'validateOptions',
     ] as $method) {
         expect($reflection->hasMethod($method))->toBeTrue();
     }
+
+    expect((string) $reflection->getMethod('key')->getReturnType())->toBe('string');
+    expect((string) $reflection->getMethod('sourceFormat')->getReturnType())->toBe('string');
+    expect((string) $reflection->getMethod('targetFormat')->getReturnType())->toBe('string');
+    expect((string) $reflection->getMethod('label')->getReturnType())->toBe('string');
+    expect((string) $reflection->getMethod('description')->getReturnType())->toBe('string');
+    expect((string) $reflection->getMethod('optionsSchema')->getReturnType())->toBe('array');
+    expect($reflection->getMethod('validateOptions')->getNumberOfParameters())->toBe(1);
+    expect((string) $reflection->getMethod('validateOptions')->getReturnType())->toBe('array');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Unit/Converters/ConverterContractTest.php` around lines 10 - 20, The
test currently only asserts the presence of methods; enhance it to assert each
method's signature by creating ReflectionMethod instances for 'key',
'sourceFormat', 'targetFormat', 'label', 'description', 'optionsSchema', and
'validateOptions' and verifying expected parameter counts via
getNumberOfParameters(), checking parameter type hints via
getParameters()[index]->getType() (or isBuiltin()/getName()), and asserting
return types with hasReturnType()/getReturnType()->getName(); for example ensure
no-arg methods return string where applicable and validateOptions accepts the
expected parameter type/count and return type—add these checks after the
existing hasMethod loop using the reflection symbols above to catch contract
drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/Support/Converters/DTO/OptionsSchemaField.php`:
- Around line 11-27: The toArray() method on OptionsSchemaField is including a
'default' key even when $this->default is null which makes validators like
OptionsValidator treat an implicit null as an explicit default; change
OptionsSchemaField::toArray() to only include the 'default' key when
$this->default !== null (e.g. build the array and conditionally set 'default' or
use a conditional spread) so that fields without a default do not have the
'default' key and downstream checks (OptionsValidator using array_key_exists)
behave correctly.

In `@app/Support/Converters/OptionsValidator.php`:
- Around line 102-109: The ensureColor method currently accepts 5- and
7-character hex strings because the regex uses a broad range {3,8}; update the
validation in ensureColor to only allow hex lengths 3, 4, 6, or 8 by replacing
the current length quantifier with an alternation that matches exactly those
lengths (keep the optional leading "#" and the same hex character class), so the
preg_match only returns true for valid 3/4/6/8-length hex colors and still
throws InvalidConverterOptionsException::becauseValueIsNotAllowed($key) for all
others.

---

Nitpick comments:
In `@tests/Unit/Converters/ConverterContractTest.php`:
- Around line 10-20: The test currently only asserts the presence of methods;
enhance it to assert each method's signature by creating ReflectionMethod
instances for 'key', 'sourceFormat', 'targetFormat', 'label', 'description',
'optionsSchema', and 'validateOptions' and verifying expected parameter counts
via getNumberOfParameters(), checking parameter type hints via
getParameters()[index]->getType() (or isBuiltin()/getName()), and asserting
return types with hasReturnType()/getReturnType()->getName(); for example ensure
no-arg methods return string where applicable and validateOptions accepts the
expected parameter type/count and return type—add these checks after the
existing hasMethod loop using the reflection symbols above to catch contract
drift.

In `@tests/Unit/Converters/OptionsSchemaFieldTest.php`:
- Around line 26-44: Add a regression test that constructs an OptionsSchemaField
without passing the default argument and asserts that toArray() does not include
a 'default' key; specifically, in OptionsSchemaFieldTest create a new case
similar to the existing "exposes toArray representation" but omit the default
parameter when instantiating OptionsSchemaField and assert the returned array
equals the expected array that lacks any 'default' entry (use
OptionsSchemaField::toArray() as the inspected method and ensure 'options',
'required', and 'help' behave as before).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fa5bd67e-8d47-4873-8370-0b4f2c902dbc

📥 Commits

Reviewing files that changed from the base of the PR and between 693793d and 24f37d4.

📒 Files selected for processing (22)
  • .coderabbit.yaml
  • app/Enums/FileFormat.php
  • app/Support/Converters/Contracts/Converter.php
  • app/Support/Converters/ConverterRegistry.php
  • app/Support/Converters/DTO/ConverterTarget.php
  • app/Support/Converters/DTO/OptionsSchemaField.php
  • app/Support/Converters/Exceptions/InvalidConverterOptionsException.php
  • app/Support/Converters/Exceptions/InvalidOptionsSchemaException.php
  • app/Support/Converters/Exceptions/UnsupportedFormatException.php
  • app/Support/Converters/OptionsSchemaValidator.php
  • app/Support/Converters/OptionsValidator.php
  • tests/Fakes/Converters/FakeJpgToWebpConverter.php
  • tests/Fakes/Converters/FakePngToJpgConverter.php
  • tests/Fakes/Converters/FakePngToPdfConverter.php
  • tests/Unit/Converters/ConverterContractTest.php
  • tests/Unit/Converters/ConverterRegistryTest.php
  • tests/Unit/Converters/ConverterTargetTest.php
  • tests/Unit/Converters/FakeConverterTest.php
  • tests/Unit/Converters/FileFormatTest.php
  • tests/Unit/Converters/OptionsSchemaFieldTest.php
  • tests/Unit/Converters/OptionsSchemaValidatorTest.php
  • tests/Unit/Converters/OptionsValidatorTest.php

Comment on lines +11 to +27
public mixed $default = null,
public array $options = [],
public bool $required = false,
public ?string $help = null,
) {}

public function toArray(): array
{
return [
'key' => $this->key,
'type' => $this->type,
'label' => $this->label,
'default' => $this->default,
'options' => $this->options,
'required' => $this->required,
'help' => $this->help,
];

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 | 🟠 Major | ⚡ Quick win

Avoid serializing an implicit null default as an actual default value.

toArray() currently always includes default, so fields with no intended default are treated as having one. Because OptionsValidator checks array_key_exists('default', $field), this changes validation behavior and can bypass required checks or feed null into type normalizers.

Suggested fix
     public function toArray(): array
     {
-        return [
+        $payload = [
             'key' => $this->key,
             'type' => $this->type,
             'label' => $this->label,
-            'default' => $this->default,
             'options' => $this->options,
             'required' => $this->required,
             'help' => $this->help,
-        ];
+        ];
+
+        if ($this->default !== null) {
+            $payload['default'] = $this->default;
+        }
+
+        return $payload;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Support/Converters/DTO/OptionsSchemaField.php` around lines 11 - 27, The
toArray() method on OptionsSchemaField is including a 'default' key even when
$this->default is null which makes validators like OptionsValidator treat an
implicit null as an explicit default; change OptionsSchemaField::toArray() to
only include the 'default' key when $this->default !== null (e.g. build the
array and conditionally set 'default' or use a conditional spread) so that
fields without a default do not have the 'default' key and downstream checks
(OptionsValidator using array_key_exists) behave correctly.

Comment on lines +102 to +109
private function ensureColor(string $key, mixed $value): string
{
if (! is_string($value) || ! preg_match('/^#?[0-9a-fA-F]{3,8}$/', $value)) {
throw InvalidConverterOptionsException::becauseValueIsNotAllowed($key);
}

return $value;
}

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 | 🟡 Minor | ⚡ Quick win

Color regex accepts invalid hex lengths (5 and 7).

{3,8} matches lengths 3–8, but valid hex colors are only 3 (RGB), 4 (RGBA), 6 (RRGGBB), or 8 (RRGGBBAA). A 5- or 7-character string like #abcde passes validation and flows downstream as a valid color.

🛡️ Proposed fix to restrict to valid lengths
-        if (! is_string($value) || ! preg_match('/^#?[0-9a-fA-F]{3,8}$/', $value)) {
+        if (! is_string($value) || ! preg_match('/^#?(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/', $value)) {
             throw InvalidConverterOptionsException::becauseValueIsNotAllowed($key);
         }
📝 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
private function ensureColor(string $key, mixed $value): string
{
if (! is_string($value) || ! preg_match('/^#?[0-9a-fA-F]{3,8}$/', $value)) {
throw InvalidConverterOptionsException::becauseValueIsNotAllowed($key);
}
return $value;
}
private function ensureColor(string $key, mixed $value): string
{
if (! is_string($value) || ! preg_match('/^#?(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/', $value)) {
throw InvalidConverterOptionsException::becauseValueIsNotAllowed($key);
}
return $value;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Support/Converters/OptionsValidator.php` around lines 102 - 109, The
ensureColor method currently accepts 5- and 7-character hex strings because the
regex uses a broad range {3,8}; update the validation in ensureColor to only
allow hex lengths 3, 4, 6, or 8 by replacing the current length quantifier with
an alternation that matches exactly those lengths (keep the optional leading "#"
and the same hex character class), so the preg_match only returns true for valid
3/4/6/8-length hex colors and still throws
InvalidConverterOptionsException::becauseValueIsNotAllowed($key) for all others.

menvil and others added 5 commits May 29, 2026 00:52
- OptionsSchemaField::toArray() now omits the "default" key when no default
  was provided, so OptionsValidator's array_key_exists("default", …) check
  no longer treats an implicit null as an explicit default.
- OptionsValidator::ensureColor() restricts hex lengths to 3, 4, 6, or 8;
  previously {3,8} let 5- and 7-character strings pass.
- Add regression test asserting toArray() omits "default" when not set.

Addresses CodeRabbit review on PR #59.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- OptionsSchemaField::toArray() now omits the "default" key when no default
  was provided, so OptionsValidator's array_key_exists("default", …) check
  no longer treats an implicit null as an explicit default.
- OptionsValidator::ensureColor() restricts hex lengths to 3, 4, 6, or 8;
  previously {3,8} let 5- and 7-character strings pass.
- Add regression test asserting toArray() omits "default" when not set.

Addresses CodeRabbit review on PR #59.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…view-config

INFRA-002: Fix CodeRabbit auto_review config
…-color-fixes

CONV-046: Fix OptionsSchemaField null default and tighten color regex
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release Triggers AI code review (CodeRabbit, Cubic)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant