with cast tests

This commit is contained in:
icefox 2026-02-18 20:29:13 -03:00
parent afb47c1977
commit 3a26a2e0c2
No known key found for this signature in database
8 changed files with 154 additions and 3 deletions

View file

@ -78,9 +78,9 @@ trait DataObject
foreach ($parameters as $parameter) { foreach ($parameters as $parameter) {
$parameterName = $parameter->reflection->getName(); $parameterName = $parameter->reflection->getName();
if ($mapper = array_first($parameter->reflection->getAttributes(CastWith::class))) { if ($castWith = array_first($parameter->reflection->getAttributes(CastWith::class))) {
$value = App::call( $value = App::call(
[App::make($mapper->newInstance()->class), 'map'], [App::make($castWith->newInstance()->class), 'cast'],
['value' => $validator->getValue($parameterName)], ['value' => $validator->getValue($parameterName)],
); );
$mappedInput[$parameterName] = $value; $mappedInput[$parameterName] = $value;

View file

@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace Tests;
use Illuminate\Validation\ValidationException;
use Tests\Casters\SimpleValue;
use Tests\Casters\SimpleValueCaster;
use Tests\Casters\WithGlobalCaster;
use Tests\Casters\WithSpecificCaster;
use Tests\Casters\WithoutCaster;
describe('caster priority', function () {
beforeEach(function () {
config(['dto.cast' => []]);
});
it('uses CastWith attribute over global config caster', function () {
$globalCaster = function (mixed $value): SimpleValue {
return new SimpleValue($value['value'] * 3);
};
config(['dto.cast.' . SimpleValue::class => $globalCaster]);
$object = WithSpecificCaster::fromArray([
'value' => ['value' => 5],
]);
expect($object->value->value)->toBe(10); // 5 * 2
});
it('falls back to global config caster when no CastWith attribute', function () {
$globalCaster = function (mixed $value): SimpleValue {
return new SimpleValue($value['value'] * 3);
};
config(['dto.cast.' . SimpleValue::class => $globalCaster]);
$object = WithGlobalCaster::fromArray([
'value' => ['value' => 5],
]);
expect($object->value->value)->toBe(15); // 5 * 3
});
it('falls back to default construction when no caster exists', function () {
$object = WithoutCaster::fromArray([
'value' => ['value' => 5],
]);
expect($object)->toBeInstanceOf(WithoutCaster::class);
});
});
describe('caster with rules', function () {
beforeEach(function () {
config(['dto.cast' => []]);
});
it('validates input using caster rules before casting', function () {
expect(fn() => WithSpecificCaster::fromArray([
'value' => [],
]))->toThrow(ValidationException::class);
});
it('accepts valid input and casts correctly', function () {
$object = WithSpecificCaster::fromArray([
'value' => ['value' => 10],
]);
expect($object->value->value)->toBe(20); // 10 * 2
});
});

View file

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Tests\Casters;
class SimpleValue
{
public function __construct(public readonly int $value) {}
}

View file

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Tests\Casters;
class SimpleValueCaster
{
public function cast(mixed $value): SimpleValue
{
return new SimpleValue($value['value'] * 2);
}
public static function rules(): array
{
return [
'value' => ['required', 'numeric'],
];
}
}

View file

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Tests\Casters;
use Icefox\DTO\DataObject;
readonly class WithGlobalCaster
{
use DataObject;
public function __construct(
public SimpleValue $value,
) {}
}

View file

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Tests\Casters;
use Icefox\DTO\Attributes\CastWith;
use Icefox\DTO\DataObject;
readonly class WithSpecificCaster
{
use DataObject;
public function __construct(
#[CastWith(SimpleValueCaster::class)]
public SimpleValue $value,
) {}
}

View file

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Tests\Casters;
use Icefox\DTO\DataObject;
readonly class WithoutCaster
{
use DataObject;
public function __construct(
public SimpleValue $value,
) {}
}

View file

@ -7,7 +7,7 @@ use Illuminate\Support\Carbon;
class CarbonPeriodMapper class CarbonPeriodMapper
{ {
public function map(mixed $value): CarbonPeriodImmutable public function cast(mixed $value): CarbonPeriodImmutable
{ {
return new CarbonPeriodImmutable(Carbon::parse($value['start']), Carbon::parse($value['end'])); return new CarbonPeriodImmutable(Carbon::parse($value['start']), Carbon::parse($value['end']));
} }