asset()); self::assertSame($minor, $amount->minor()); self::assertSame($decimal, $amount->decimal()); } public static function validBalanceProvider(): array { return [ 'zero' => ['0', 0, '0.00'], 'one decimal' => ['12.3', 1230, '12.30'], 'two decimals' => ['12.34', 1234, '12.34'], 'integer' => ['999', 99900, '999.00'], ]; } #[DataProvider('invalidBalanceProvider')] public function test_balance_rejects_non_canonical_input(string $input): void { $this->expectException(InvalidArgumentException::class); AssetAmount::balance($input); } public static function invalidBalanceProvider(): array { return [ 'negative' => ['-1.00'], 'float precision' => ['1.001'], 'scientific notation' => ['1e2'], 'leading zero' => ['01.00'], 'leading decimal' => ['.50'], 'trailing decimal' => ['1.'], 'plus sign' => ['+1.00'], 'comma' => ['1,00'], 'empty' => [''], ]; } public function test_balance_rejects_integer_overflow(): void { $this->expectException(InvalidArgumentException::class); AssetAmount::balance((string) PHP_INT_MAX . '.99'); } public function test_points_are_integer_minor_units(): void { self::assertSame(37, AssetAmount::points(37)->minor()); self::assertSame('37', AssetAmount::points(37)->decimal()); } public function test_negative_points_are_rejected(): void { $this->expectException(InvalidArgumentException::class); AssetAmount::points(-1); } public function test_balance_minor_helpers_do_not_use_floats(): void { self::assertSame(1234, AssetAmount::balance_minor('12.34')); self::assertSame('12.34', AssetAmount::decimal_from_minor(1234)); } public function test_percentage_rounds_half_up_in_minor_units(): void { self::assertSame(266, AssetAmount::percentage(333, 80)); self::assertSame(1, AssetAmount::percentage(1, 50)); self::assertSame(0, AssetAmount::percentage(1, 49)); } public function test_percentage_rejects_out_of_range_values(): void { $this->expectException(InvalidArgumentException::class); AssetAmount::percentage(100, 101); } public function test_formatted_adds_thousands_separators_to_balance(): void { self::assertSame('0.00', AssetAmount::balance('0')->formatted()); self::assertSame('12.34', AssetAmount::balance('12.34')->formatted()); self::assertSame('564,000.00', AssetAmount::balance('564000')->formatted()); self::assertSame('1,234,567.89', AssetAmount::balance('1234567.89')->formatted()); self::assertSame('37', AssetAmount::points(37)->formatted()); } }