lowPart())->toBe(10000) ->and($portion->highPart())->toBe(15000) ->and($portion->approximative())->toBeTrue(); }); }); describe('PortionAmount Value Object Domain Rules', function (): void { it('throws DomainException for negative lowPart', function (): void { // GIVEN, WHEN & THEN expect(fn () => new PortionAmount(-1.0)) ->toThrow(\DomainException::class, 'lowPart must be between 0 and'); }); it('throws DomainException for negative highPart', function (): void { // GIVEN, WHEN & THEN expect(fn () => new PortionAmount(100.0, -1.0)) ->toThrow(\DomainException::class, 'highPart must be between 0 and'); }); it('throws DomainException for lowPart greater than highPart', function (): void { // GIVEN, WHEN & THEN expect(fn () => new PortionAmount(150.0, 100.0)) ->toThrow(\DomainException::class, 'lowPart cannot be greater than highPart'); }); it('throws DomainException for lowPart exceeding maximum value', function (): void { // GIVEN $maxMask = (1 << 15) - 1; $scale = (int) pow(10, 2); $maxValue = (float) bcdiv((string) $maxMask, (string) $scale, 2); // WHEN & THEN expect(fn () => new PortionAmount($maxValue + 0.01)) ->toThrow(\DomainException::class, "lowPart must be between 0 and '{$maxValue}'"); }); it('throws DomainException for highPart exceeding maximum value', function (): void { // GIVEN $maxMask = (1 << 15) - 1; $scale = (int) pow(10, 2); $maxValue = (float) bcdiv((string) $maxMask, (string) $scale, 2); // WHEN & THEN expect(fn () => new PortionAmount(100.0, $maxValue + 0.01)) ->toThrow(\DomainException::class, "highPart must be between 0 and '{$maxValue}'"); }); });