Files
HickLab3D_Dashboard/tests/Unit/FixedPointTest.php
T
hermes d4928789c4
Deploy HickLab3D Dashboard / Deploy to production (push) Failing after 23s
Add HL3D branding, Etsy Live/Draft paging and Projects lifecycle
Replace the old touchmark/PNG identity with the supplied lockup and
monogram, segmented role-aware navigation, Live/Draft listing filters
with generation-based cache invalidation, and admin Projects covering
immutable file revisions, listing live pointers, and exact-decimal
filament/packaging/shipping costing. Additive schema only; no Etsy
publication. Isolated clone tests: 301 tests, 1162 assertions.
2026-11-08 12:50:56 +00:00

103 lines
3.7 KiB
PHP

<?php
namespace Tests\Unit;
use App\Support\FixedPoint;
use InvalidArgumentException;
use OverflowException;
use Tests\TestCase;
class FixedPointTest extends TestCase
{
public function test_parses_decimal_string_to_scaled_integer(): void
{
$this->assertSame(1500, FixedPoint::toScaled('1.5', 3));
$this->assertSame(-1500, FixedPoint::toScaled('-1.5', 3));
$this->assertSame(1000000, FixedPoint::toScaled('1', 6));
$this->assertSame(0, FixedPoint::toScaled('0', 4));
}
public function test_rejects_malformed_decimal_strings(): void
{
$this->expectException(InvalidArgumentException::class);
FixedPoint::toScaled('1.5.3', 3);
}
public function test_rejects_non_numeric_strings(): void
{
$this->expectException(InvalidArgumentException::class);
FixedPoint::toScaled('abc', 3);
}
public function test_rejects_values_exceeding_bound(): void
{
$this->expectException(InvalidArgumentException::class);
FixedPoint::toScaled('100000000', 6, maxAbs: 1_000_000);
}
public function test_rejects_negative_values_when_disallowed(): void
{
$this->expectException(InvalidArgumentException::class);
FixedPoint::toScaled('-5', 3, allowNegative: false);
}
public function test_multiplies_two_scaled_values_and_rescales_with_round_half_up(): void
{
// 2.5 grams (scale 3) * 4.0 rate per kg (scale 6) => product scale 9, then /1000 for g->kg => rescale to scale 4.
$grams = FixedPoint::toScaled('2.500', 3);
$rate = FixedPoint::toScaled('4.000000', 6);
$productScale9 = FixedPoint::multiply($grams, $rate);
// raw cost = 2.5g * (4.0/1000 per g) = 0.01 -> scale 4 => 100
$result = FixedPoint::rescale($productScale9, 9 + 3, 4);
$this->assertSame(100, $result);
$this->assertSame('0.0100', FixedPoint::format($result, 4));
}
public function test_rescale_rounds_half_up(): void
{
// 0.00005 at scale 5 rescaled to scale 4 should round up to 0.0001 (not banker's rounding).
$this->assertSame(1, FixedPoint::rescale(5, 5, 4));
$this->assertSame(0, FixedPoint::rescale(4, 5, 4));
$this->assertSame(-1, FixedPoint::rescale(-5, 5, 4));
}
public function test_add_and_subtract_scaled_values(): void
{
$a = FixedPoint::toScaled('10.1234', 4);
$b = FixedPoint::toScaled('0.0001', 4);
$this->assertSame(101235, FixedPoint::add($a, $b));
$this->assertSame(101233, FixedPoint::subtract($a, $b));
}
public function test_add_detects_overflow(): void
{
$this->expectException(OverflowException::class);
FixedPoint::add(PHP_INT_MAX, 1);
}
public function test_multiply_detects_overflow(): void
{
$this->expectException(OverflowException::class);
FixedPoint::multiply(PHP_INT_MAX, 2);
}
public function test_divide_scaled_value_by_positive_integer_with_round_half_up(): void
{
$amount = FixedPoint::toScaled('10.0000', 4);
$this->assertSame(FixedPoint::toScaled('3.3333', 4), FixedPoint::divideByInt($amount, 3));
}
public function test_divide_by_zero_is_rejected(): void
{
$this->expectException(InvalidArgumentException::class);
FixedPoint::divideByInt(FixedPoint::toScaled('1', 4), 0);
}
public function test_format_renders_fixed_decimal_places_without_floats(): void
{
$this->assertSame('1234.5000', FixedPoint::format(FixedPoint::toScaled('1234.5', 4), 4));
$this->assertSame('-0.0100', FixedPoint::format(FixedPoint::toScaled('-0.01', 4), 4));
$this->assertSame('0.0000', FixedPoint::format(0, 4));
}
}