tests
This commit is contained in:
parent
6b1a385292
commit
bba10b455f
10 changed files with 296 additions and 514 deletions
|
|
@ -4,9 +4,14 @@ declare(strict_types=1);
|
|||
|
||||
namespace Tests;
|
||||
|
||||
use Carbon\CarbonPeriod;
|
||||
use Icefox\DTO\Attributes\CastWith;
|
||||
use Icefox\DTO\Attributes\Flat;
|
||||
use Icefox\DTO\Attributes\FromInput;
|
||||
use Icefox\DTO\Attributes\Overwrite;
|
||||
use Icefox\DTO\RuleFactory;
|
||||
use Icefox\DTO\ValueFactory;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
readonly class BasicPrimitives
|
||||
|
|
@ -16,150 +21,202 @@ readonly class BasicPrimitives
|
|||
public int $number,
|
||||
public bool $flag,
|
||||
public ?array $items,
|
||||
public float $floating = 0.0,
|
||||
public float $floating = 4.7,
|
||||
) {}
|
||||
}
|
||||
|
||||
test('required rules', function () {
|
||||
test('basic creation works', function () {
|
||||
$object = ValueFactory::make(BasicPrimitives::class, [
|
||||
'text' => 'abc',
|
||||
'number' => 42,
|
||||
'flag' => false,
|
||||
'items' => ['a', 2, true],
|
||||
'floating' => 4.2,
|
||||
'flag' => true,
|
||||
'items' => ['a', 2, false],
|
||||
'floating' => 32.6,
|
||||
]);
|
||||
|
||||
expect($object->text)->toBe('abc');
|
||||
expect($object->number)->toBe(42);
|
||||
expect($object->flag)->toBe(false);
|
||||
expect($object->items)->toBe(['a', 2, true]);
|
||||
expect($object->floating)->toEqualWithDelta(4.2, 0.000001);
|
||||
expect($object->flag)->toBe(true);
|
||||
expect($object->items)->toBe(['a', 2, false]);
|
||||
expect($object->floating)->toBe(32.6);
|
||||
});
|
||||
|
||||
readonly class AnnotatedArray
|
||||
{
|
||||
/**
|
||||
* @param array<int,float> $items
|
||||
*/
|
||||
public function __construct(public array $items) {}
|
||||
}
|
||||
test('annotated array', function () {
|
||||
expect(RuleFactory::instance()->make(AnnotatedArray::class))->toBe([
|
||||
'items' => ['required', 'array'],
|
||||
'items.*' => ['required', 'numeric'],
|
||||
test('uses default values', function () {
|
||||
$object = ValueFactory::make(BasicPrimitives::class, [
|
||||
'text' => 'abc',
|
||||
'number' => 42,
|
||||
'flag' => true,
|
||||
'items' => ['a', 2, false],
|
||||
]);
|
||||
|
||||
expect($object->text)->toBe('abc');
|
||||
expect($object->number)->toBe(42);
|
||||
expect($object->flag)->toBe(true);
|
||||
expect($object->items)->toBe(['a', 2, false]);
|
||||
expect($object->floating)->toBe(4.7);
|
||||
});
|
||||
|
||||
readonly class AnnotatedArrayNullableValue
|
||||
{
|
||||
/**
|
||||
* @param array<?float> $items
|
||||
*/
|
||||
public function __construct(public array $items) {}
|
||||
}
|
||||
test('annotated array with nullable items', function () {
|
||||
expect(RuleFactory::instance()->make(AnnotatedArrayNullableValue::class))->toBe([
|
||||
'items' => ['required', 'array'],
|
||||
'items.*' => ['nullable', 'numeric'],
|
||||
test('uses default when null and not nullable', function () {
|
||||
$object = ValueFactory::make(BasicPrimitives::class, [
|
||||
'text' => 'abc',
|
||||
'number' => 42,
|
||||
'flag' => true,
|
||||
'items' => ['a', 2, false],
|
||||
'floating' => null,
|
||||
]);
|
||||
|
||||
expect($object->text)->toBe('abc');
|
||||
expect($object->number)->toBe(42);
|
||||
expect($object->flag)->toBe(true);
|
||||
expect($object->items)->toBe(['a', 2, false]);
|
||||
expect($object->floating)->toBe(4.7);
|
||||
});
|
||||
|
||||
readonly class PlainLeaf
|
||||
{
|
||||
public function __construct(public string $name) {}
|
||||
}
|
||||
readonly class PlainRoot
|
||||
{
|
||||
public function __construct(public int $value, public PlainLeaf $leaf) {}
|
||||
}
|
||||
|
||||
test('plain nesting', function () {
|
||||
expect(RuleFactory::instance()->make(PlainRoot::class))->toBe([
|
||||
'value' => ['required', 'numeric'],
|
||||
'leaf' => ['required'],
|
||||
'leaf.name' => ['required'],
|
||||
test('accepts null when nullable', function () {
|
||||
$object = ValueFactory::make(BasicPrimitives::class, [
|
||||
'text' => 'abc',
|
||||
'number' => 42,
|
||||
'flag' => true,
|
||||
'items' => null,
|
||||
]);
|
||||
|
||||
expect($object->text)->toBe('abc');
|
||||
expect($object->number)->toBe(42);
|
||||
expect($object->flag)->toBe(true);
|
||||
expect($object->items)->toBe(null);
|
||||
expect($object->floating)->toBe(4.7);
|
||||
});
|
||||
|
||||
test('accepts missing as null when nullable', function () {
|
||||
$object = ValueFactory::make(BasicPrimitives::class, [
|
||||
'text' => 'abc',
|
||||
'number' => 42,
|
||||
'flag' => true,
|
||||
]);
|
||||
|
||||
expect($object->text)->toBe('abc');
|
||||
expect($object->number)->toBe(42);
|
||||
expect($object->flag)->toBe(true);
|
||||
expect($object->items)->toBe(null);
|
||||
expect($object->floating)->toBe(4.7);
|
||||
});
|
||||
|
||||
readonly class NestedLeaf
|
||||
{
|
||||
public function __construct(public bool $flag) {}
|
||||
}
|
||||
|
||||
readonly class RootWithNestedLeaf
|
||||
{
|
||||
public function __construct(public int $value, public NestedLeaf $leaf) {}
|
||||
}
|
||||
|
||||
test('creates nested object', function () {
|
||||
$root = ValueFactory::make(RootWithNestedLeaf::class, [
|
||||
'value' => 42,
|
||||
'leaf' => [
|
||||
'flag' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
expect($root->value)->toBe(42);
|
||||
expect($root->leaf->flag)->toBe(true);
|
||||
});
|
||||
|
||||
|
||||
readonly class AnnotatedArrayItem
|
||||
readonly class CollectionItem
|
||||
{
|
||||
public function __construct(public int $value) {}
|
||||
}
|
||||
|
||||
readonly class AnnotatedArrayObject
|
||||
readonly class CollectionRoot
|
||||
{
|
||||
/**
|
||||
* @param ?array<AnnotatedArrayItem> $items
|
||||
* @param Collection<int, CollectionItem> $items
|
||||
*/
|
||||
public function __construct(public ?array $items) {}
|
||||
public function __construct(public string $text, public Collection $items) {}
|
||||
}
|
||||
|
||||
test('annotated array with object', function () {
|
||||
expect(RuleFactory::instance()->make(AnnotatedArrayObject::class))->toBe([
|
||||
'items' => ['nullable', 'array'],
|
||||
'items.*' => ['required'],
|
||||
'items.*.value' => ['required', 'numeric'],
|
||||
test('creates collection object', function () {
|
||||
$root = ValueFactory::make(CollectionRoot::class, [
|
||||
'text' => 'abc',
|
||||
'items' => [
|
||||
[ 'value' => 1 ],
|
||||
[ 'value' => 2 ],
|
||||
[ 'value' => 4 ],
|
||||
[ 'value' => 8 ],
|
||||
],
|
||||
]);
|
||||
|
||||
expect($root->text)->toBe('abc');
|
||||
expect($root->items)->toBeInstanceOf(Collection::class);
|
||||
expect($root->items->count())->toBe(4);
|
||||
expect($root->items[0]->value)->toBe(1);
|
||||
expect($root->items[1]->value)->toBe(2);
|
||||
expect($root->items[2]->value)->toBe(4);
|
||||
expect($root->items[3]->value)->toBe(8);
|
||||
});
|
||||
|
||||
readonly class FlattenedLeaf
|
||||
readonly class DoubleCast
|
||||
{
|
||||
public function __construct(public ?bool $flag) {}
|
||||
public static function cast(int $data): int
|
||||
{
|
||||
return $data * 2;
|
||||
}
|
||||
}
|
||||
|
||||
readonly class NotFlattenedLeaf
|
||||
{
|
||||
public function __construct(public string $description) {}
|
||||
}
|
||||
|
||||
readonly class FlattenedNode
|
||||
{
|
||||
public function __construct(
|
||||
public string $id,
|
||||
public NotFlattenedLeaf $leaf,
|
||||
#[Flat]
|
||||
public FlattenedLeaf $squish,
|
||||
public int $level = 1,
|
||||
) {}
|
||||
}
|
||||
|
||||
readonly class FlattenedRoot
|
||||
readonly class WithExplicitCast
|
||||
{
|
||||
public function __construct(
|
||||
#[CastWith(DoubleCast::class)]
|
||||
public int $value,
|
||||
#[Flat]
|
||||
public FlattenedNode $node,
|
||||
) {}
|
||||
}
|
||||
|
||||
test('flattened basic', function () {
|
||||
expect(RuleFactory::instance()->make(FlattenedRoot::class))->toBe([
|
||||
'value' => ['required', 'numeric'],
|
||||
'id' => ['required' ],
|
||||
'leaf' => ['required'],
|
||||
'leaf.description' => ['required'],
|
||||
'flag' => ['nullable', 'boolean'],
|
||||
'level' => ['sometimes', 'numeric'],
|
||||
]);
|
||||
});
|
||||
|
||||
readonly class AnnotatedCollectionItem
|
||||
{
|
||||
public function __construct(public int $value) {}
|
||||
}
|
||||
|
||||
readonly class AnnotatedCollection
|
||||
readonly class WithNestedCast
|
||||
{
|
||||
/**
|
||||
* @param Collection<AnnotatedCollectionItem> $group
|
||||
* @param array<int,WithExplicitCast> $items
|
||||
*/
|
||||
public function __construct(public Collection $group) {}
|
||||
public function __construct(public array $items) {}
|
||||
}
|
||||
|
||||
test('annotated collection', function () {
|
||||
expect(RuleFactory::instance()->make(AnnotatedCollection::class))->toBe([
|
||||
'group' => ['required', 'array'],
|
||||
'group.*' => ['required'],
|
||||
'group.*.value' => ['required', 'numeric'],
|
||||
]);
|
||||
test('with explicit cast', function () {
|
||||
$object = ValueFactory::make(WithExplicitCast::class, ['value' => 32]);
|
||||
expect($object->value)->toBe(64);
|
||||
});
|
||||
|
||||
test('with nested cast', function () {
|
||||
$object = ValueFactory::make(WithNestedCast::class, ['items' => [ ['value' => 2], ['value' => 3], ['value' => 5]]]);
|
||||
|
||||
expect($object->items[0]->value)->toBe(4);
|
||||
expect($object->items[1]->value)->toBe(6);
|
||||
expect($object->items[2]->value)->toBe(10);
|
||||
});
|
||||
|
||||
readonly class CarbonPeriodCast
|
||||
{
|
||||
/**
|
||||
* @param array<int,mixed> $data
|
||||
*/
|
||||
public static function cast(array $data): CarbonPeriod
|
||||
{
|
||||
return new CarbonPeriod(Carbon::parse($data['start']), Carbon::parse($data['end']));
|
||||
}
|
||||
}
|
||||
|
||||
readonly class WithObjectCast
|
||||
{
|
||||
public function __construct(
|
||||
#[CastWith(CarbonPeriodCast::class)]
|
||||
public CarbonPeriod $period,
|
||||
) {}
|
||||
}
|
||||
|
||||
test('with object cast', function () {
|
||||
$object = ValueFactory::make(WithObjectCast::class, ['period' => ['start' => '1980-10-01', 'end' => '1990-06-01']]);
|
||||
|
||||
expect($object->period)->toBeInstanceOf(CarbonPeriod::class);
|
||||
expect($object->period->start->format('Y-m-d'))->toBe('1980-10-01');
|
||||
expect($object->period->end->format('Y-m-d'))->toBe('1990-06-01');
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue