toBeInstanceOf(DiscountValue::class); expect($vo->isPercentage())->toBeFalse(); }); it('can instantiate from percentage', function () { // GIVEN $rate = 20; // WHEN $vo = DiscountValue::fromPercentage($rate); // THEN expect($vo)->toBeInstanceOf(DiscountValue::class); expect($vo->isPercentage())->toBeTrue(); }); it('throws exception if percentage is negative', function () { // GIVEN // WHEN $action = fn () => DiscountValue::fromPercentage(-1); // THEN expect($action)->toThrow(\DomainException::class); }); it('throws exception if percentage is greater than 100', function () { // GIVEN $rate = 101; // WHEN $action = fn () => DiscountValue::fromPercentage($rate); // THEN expect($action)->toThrow(\DomainException::class); }); it('throws exception if amount is negative', function () { // GIVEN $amount = getDiscountMoney(-1); // WHEN $action = fn () => DiscountValue::fromAmount($amount); // THEN expect($action)->toThrow(\DomainException::class); }); it('returns the correct value for amount discount', function () { // GIVEN $amount = getDiscountMoney(50); // WHEN $vo = DiscountValue::fromAmount($amount); // THEN expect($vo->value())->toEqual($amount); }); it('returns the correct value for percentage discount', function () { // GIVEN $rate = 20; // WHEN $vo = DiscountValue::fromPercentage($rate); // THEN expect($vo->value())->toBe($rate); }); it('returns the correct percentage rate', function () { // GIVEN $percentage = 20; // WHEN $vo = DiscountValue::fromPercentage($percentage); // THEN expect($vo->getPercentageRate())->toBe(0.2); }); it('throws exception when getting percentage rate from amount discount', function () { // GIVEN $amount = getDiscountMoney(100); $vo = DiscountValue::fromAmount($amount); // WHEN $action = fn () => $vo->getPercentageRate(); // THEN expect($action)->toThrow(\DomainException::class); }); it('returns the correct amount', function () { // GIVEN $amount = getDiscountMoney(100); // WHEN $vo = DiscountValue::fromAmount($amount); // THEN expect($vo->getAmount())->toEqual($amount); }); it('throws exception when getting amount from percentage discount', function () { // GIVEN $vo = DiscountValue::fromPercentage(10); // WHEN $action = fn () => $vo->getAmount(); // THEN expect($action)->toThrow(\DomainException::class); }); it('calculates discount amount correctly for percentage', function () { // GIVEN $basePrice = getDiscountMoney(1000); $vo = DiscountValue::fromPercentage(20); // 20% // WHEN $result = $vo->calculateDiscountAmount($basePrice); // THEN expect($result->getAmount())->toBe('200'); }); it('calculates discount amount correctly for fixed amount', function () { // GIVEN $basePrice = getDiscountMoney(1000); $vo = DiscountValue::fromAmount(getDiscountMoney(50)); // WHEN $result = $vo->calculateDiscountAmount($basePrice); // THEN expect($result->getAmount())->toBe('50'); }); it('throws exception on currency mismatch when calculating discount', function () { // GIVEN $basePrice = getDiscountMoney(1000, 'EUR'); $vo = DiscountValue::fromAmount(getDiscountMoney(50, 'USD')); // WHEN $action = fn () => $vo->calculateDiscountAmount($basePrice); // THEN expect($action)->toThrow(\DomainException::class); }); }); describe('DiscountValue exceptions', function (): void { it('throws when percentage is missing for percent type', function (): void { // GIVEN $reflection = new \ReflectionClass(DiscountValue::class); $method = $reflection->getMethod('__construct'); $vo = $reflection->newInstanceWithoutConstructor(); // WHEN $action = fn () => $method->invoke($vo, DiscountType::PERCENT, null, null); // THEN expect($action)->toThrow(\DomainException::class); }); it('throws when amount is missing for amount type', function (): void { // GIVEN $reflection = new \ReflectionClass(DiscountValue::class); $method = $reflection->getMethod('__construct'); $vo = $reflection->newInstanceWithoutConstructor(); // WHEN $action = fn () => $method->invoke($vo, DiscountType::AMOUNT, null, null); // THEN expect($action)->toThrow(\DomainException::class); }); it('throws unexpected value if percentage is null', function (): void { // GIVEN $reflection = new \ReflectionClass(DiscountValue::class); $vo = $reflection->newInstanceWithoutConstructor(); $prop = $reflection->getProperty('type'); $prop->setValue($vo, DiscountType::PERCENT); $prop2 = $reflection->getProperty('percentage'); $prop2->setValue($vo, null); // WHEN $action = fn () => $vo->getPercentage(); // THEN expect($action)->toThrow(\DomainException::class); }); it('throws unexpected value if amount is null', function (): void { // GIVEN $reflection = new \ReflectionClass(DiscountValue::class); $vo = $reflection->newInstanceWithoutConstructor(); $prop = $reflection->getProperty('type'); $prop->setValue($vo, DiscountType::AMOUNT); $prop2 = $reflection->getProperty('amount'); $prop2->setValue($vo, null); // WHEN $action = fn () => $vo->getAmount(); // THEN expect($action)->toThrow(\DomainException::class); }); });