'abc', 'number' => 42, 'flag' => true, 'items' => ['a', 2, false], 'floating' => 32.6, ]); 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(32.6); }); 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); }); 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); }); 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 CollectionItem { public function __construct(public int $value) {} } readonly class CollectionRoot { /** * @param Collection $items */ public function __construct(public string $text, public Collection $items) {} } 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 DoubleCast { public static function cast(int $data): int { return $data * 2; } } readonly class WithExplicitCast { public function __construct( #[CastWith(DoubleCast::class)] public int $value, ) {} } readonly class WithNestedCast { /** * @param array $items */ public function __construct(public array $items) {} } 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 $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'); });