230 lines
6.6 KiB
PHP
230 lines
6.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Unit\Domain\Models\ValueObjects;
|
|
|
|
use Domain\Enums\DiscountType;
|
|
use Domain\Models\ValueObjects\DiscountValue;
|
|
use Money\Currency;
|
|
use Money\Money;
|
|
|
|
describe('DiscountValue', function () {
|
|
|
|
function getDiscountMoney(int $amount, string $currency = 'EUR'): Money
|
|
{
|
|
return new Money($amount, new Currency($currency));
|
|
}
|
|
|
|
it('can instantiate from amount', function () {
|
|
// GIVEN
|
|
$amount = getDiscountMoney(100);
|
|
|
|
// WHEN
|
|
$vo = DiscountValue::fromAmount($amount);
|
|
|
|
// THEN
|
|
expect($vo)->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);
|
|
});
|
|
});
|