Add HL3D branding, Etsy Live/Draft paging and Projects lifecycle
Deploy HickLab3D Dashboard / Deploy to production (push) Failing after 23s
Deploy HickLab3D Dashboard / Deploy to production (push) Failing after 23s
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.
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
<?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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user