ldc-boutique-cnc-sable/tests/Unit/Domain/Models/ValueObjects/DiscountRateTest.php

154 lines
4.6 KiB
PHP
Raw Permalink Normal View History

<?php
declare(strict_types=1);
use Domain\Models\ValueObjects\DiscountRate;
use Money\Money;
describe(DiscountRate::class, function (): void {
it('instantiates from basis points and exposes ratio and percentage', function (): void {
// GIVEN
$basisPoints = 1550;
// WHEN
$rate = DiscountRate::fromBasisPoints($basisPoints);
// THEN
expect($rate->basisPoints())->toBe(1550)
->and($rate->ratio())->toBe(0.155)
->and($rate->percentage())->toBe(15.5)
->and($rate->isZero())->toBeFalse();
});
it('creates a zero rate', function (): void {
// GIVEN / WHEN
$rate = DiscountRate::zero();
// THEN
expect($rate->basisPoints())->toBe(0)
->and($rate->isZero())->toBeTrue();
});
it('creates a rate from a percentage', function (int | float $percentage, int $expectedBasisPoints): void {
// GIVEN / WHEN
$rate = DiscountRate::fromPercentage($percentage);
// THEN
expect($rate->basisPoints())->toBe($expectedBasisPoints);
})->with([
'integer percent' => [15, 1500],
'decimal percent' => [5.5, 550],
'zero' => [0, 0],
'full' => [100, 10000],
]);
it('creates a rate from a ratio without float drift', function (float $ratio, int $expectedBasisPoints): void {
// GIVEN / WHEN
$rate = DiscountRate::fromRatio($ratio);
// THEN
expect($rate->basisPoints())->toBe($expectedBasisPoints);
})->with([
'ten percent' => [0.1, 1000],
'classic drift' => [0.29, 2900], // 0.29 * 10000 = 2899.9999999999995 in IEEE 754
'five point five' => [0.055, 550],
]);
it('computes the rate of a fixed amount discount on a base price', function (): void {
// GIVEN
$basePrice = Money::EUR(1000);
$discount = Money::EUR(200);
// WHEN
$rate = DiscountRate::fromAmounts($discount, $basePrice);
// THEN
expect($rate->basisPoints())->toBe(2000)
->and($rate->ratio())->toBe(0.2);
});
it('rounds the rate of a fixed amount discount to the nearest basis point', function (): void {
// GIVEN
$basePrice = Money::EUR(300);
$discount = Money::EUR(100); // 33.333...%
// WHEN
$rate = DiscountRate::fromAmounts($discount, $basePrice);
// THEN
expect($rate->basisPoints())->toBe(3333);
});
it('caps a fixed amount discount greater than the base price at 100 percent', function (): void {
// GIVEN
$basePrice = Money::EUR(500);
$discount = Money::EUR(900);
// WHEN
$rate = DiscountRate::fromAmounts($discount, $basePrice);
// THEN
expect($rate->basisPoints())->toBe(10000);
});
it('returns a zero rate when the base price or the discount is not positive', function (): void {
// GIVEN / WHEN
$noPrice = DiscountRate::fromAmounts(Money::EUR(100), Money::EUR(0));
$noDiscount = DiscountRate::fromAmounts(Money::EUR(0), Money::EUR(1000));
// THEN
expect($noPrice->isZero())->toBeTrue()
->and($noDiscount->isZero())->toBeTrue();
});
it('applies the rate to an amount', function (): void {
// GIVEN
$rate = DiscountRate::fromPercentage(15);
$amount = Money::EUR(1000);
// WHEN
$discount = $rate->applyTo($amount);
// THEN
expect($discount->getAmount())->toBe('150');
});
it('formats the rate as a percentage', function (int $basisPoints, string $expected): void {
// GIVEN
$rate = DiscountRate::fromBasisPoints($basisPoints);
// WHEN
$formatted = (string) $rate;
// THEN
expect($formatted)->toBe($expected);
})->with([
'integer' => [1500, '15 %'],
'decimal' => [550, '5.5 %'],
'zero' => [0, '0 %'],
'hundred' => [10000, '100 %'],
]);
it('compares two rates', function (): void {
// GIVEN
$a = DiscountRate::fromPercentage(10);
$b = DiscountRate::fromRatio(0.1);
$c = DiscountRate::fromPercentage(20);
// WHEN / THEN
expect($a->equals($b))->toBeTrue()
->and($a->equals($c))->toBeFalse();
});
it('throws when the rate is negative or above 100 percent', function (int $basisPoints): void {
// GIVEN / WHEN
$action = fn () => DiscountRate::fromBasisPoints($basisPoints);
// THEN
expect($action)->toThrow(\InvalidArgumentException::class);
})->with([
'negative' => [-1],
'above 100%' => [10001],
]);
});