data-transfer-object/tests/DataObject/DataObjectTest.php
2026-02-27 11:14:42 -03:00

195 lines
5 KiB
PHP

<?php
namespace Tests\DataObject;
use Icefox\DTO\Attributes\FromInput;
use Icefox\DTO\Attributes\FromRouteParameter;
use Icefox\DTO\Factories\DataObjectFactory;
use Illuminate\Support\Collection;
use Psr\Log\NullLogger;
readonly class Element
{
public function __construct(public int $value) {}
}
readonly class Node
{
public function __construct(public Element $element) {}
}
test('basic nested object', function () {
$input = DataObjectFactory::mapInput(Node::class, ['element' => ['value' => 1 ] ], [], new NullLogger());
expect($input)->toBe(['element' => ['value' => 1]]);
});
readonly class MappedElement
{
public function __construct(#[FromInput('name')] public int $value) {}
}
readonly class MappedNode
{
public function __construct(public MappedElement $element) {}
}
test('basic nested input map', function () {
$input = DataObjectFactory::mapInput(MappedNode::class, ['element' => ['name' => 1 ] ], [], new NullLogger());
expect($input)->toBe(['element' => ['value' => 1]]);
});
readonly class MappedCollectionItem
{
public function __construct(#[FromInput('id_item')] public int $idItem) {}
}
readonly class MappedCollectionRoot
{
/**
* @param Collection<int, MappedCollectionItem> $items
*/
public function __construct(public string $text, #[FromInput('data')] public Collection $items) {}
}
test('using from input nested', function () {
$mapped = DataObjectFactory::mapInput(MappedCollectionRoot::class, [
'text' => 'abc',
'data' => [
[ 'id_item' => 1 ],
[ 'id_item' => 2 ],
[ 'id_item' => 4 ],
[ 'id_item' => 8 ],
],
], [], new NullLogger());
expect($mapped)->toBe([
'text' => 'abc',
'items' => [
[ 'idItem' => 1 ],
[ 'idItem' => 2 ],
[ 'idItem' => 4 ],
[ 'idItem' => 8 ],
],
]);
});
readonly class CollectionRoot
{
/**
* @param Collection<int, MappedCollectionItem> $items
*/
public function __construct(public string $text, public Collection $items) {}
}
test('using from input', function () {
$mapped = DataObjectFactory::mapInput(MappedCollectionRoot::class, [
'text' => 'abc',
'items' => [
[ 'id_item' => 1 ],
[ 'id_item' => 2 ],
[ 'id_item' => 4 ],
[ 'id_item' => 8 ],
],
], [], new NullLogger());
expect($mapped)->toBe([
'text' => 'abc',
'items' => [
[ 'idItem' => 1 ],
[ 'idItem' => 2 ],
[ 'idItem' => 4 ],
[ 'idItem' => 8 ],
],
]);
});
readonly class AnnotatedArrayItem
{
public function __construct(#[FromInput('name')] public float $value) {}
}
readonly class AnnotatedArray
{
/**
* @param array<int,AnnotatedArrayItem> $items
*/
public function __construct(public array $items) {}
}
test('annotated array', function () {
$mapped = DataObjectFactory::mapInput(
AnnotatedArray::class,
['items' => [['name' => 1], ['name' => 2]]],
[],
new NullLogger(),
);
expect($mapped)->toBe(['items' => [['value' => 1], ['value' => 2]]]);
});
test('route parameter priority over from input', function () {
$dto = new class (123) {
public function __construct(
#[FromRouteParameter('user_id')]
#[FromInput('user_id')]
public int $id,
) {}
};
$result = DataObjectFactory::mapInput(
$dto::class,
['user_id' => 456],
['user_id' => 123],
new \Psr\Log\NullLogger(),
);
expect($result['id'])->toBe(123);
});
test('multiple route parameters', function () {
$dto = new class (45, 89, 'A') {
public function __construct(
#[FromRouteParameter('course_id')]
public int $courseId,
#[FromRouteParameter('student_id')]
public int $studentId,
#[FromInput('grade')]
public string $grade,
) {}
};
$result = DataObjectFactory::mapInput(
$dto::class,
['grade' => 'A'],
['course_id' => 45, 'student_id' => 89],
new \Psr\Log\NullLogger(),
);
expect($result['courseId'])->toBe(45)
->and($result['studentId'])->toBe(89)
->and($result['grade'])->toBe('A');
});
test('route parameter with nested object', function () {
$addressDto = new class ('Main St') {
public function __construct(public string $street) {}
};
$dto = new class (123, $addressDto) {
public function __construct(
#[FromRouteParameter('user_id')]
public int $userId,
public $address,
) {}
};
$result = DataObjectFactory::mapInput(
$dto::class,
['user_id' => 456, 'address' => ['street' => 'Main St']],
['user_id' => 123],
new \Psr\Log\NullLogger(),
);
expect($result['userId'])->toBe(123)
->and($result['address']['street'])->toBe('Main St');
});