Files

116 lines
4.0 KiB
PHP

<?php
use AKSRT\Ledger\AssetType;
use AKSRT\Ledger\LedgerCommand;
use PHPUnit\Framework\TestCase;
final class LedgerCommandTest extends TestCase {
public function test_transfer_splits_principal_fee_and_net_receipt(): void {
$command = LedgerCommand::transfer(
'client-transfer:1:550e8400-e29b-41d4-a716-446655440000',
'transfer',
10,
20,
AssetType::BALANCE,
1000,
125
);
self::assertSame([-875, -125, 875], array_column($command->entries, 'delta_minor'));
self::assertSame(['transfer_out', 'fee', 'transfer_in'], array_column($command->entries, 'entry_type'));
self::assertSame(1000, $command->metadata['gross_minor']);
self::assertSame(125, $command->metadata['fee_minor']);
self::assertSame(875, $command->metadata['net_minor']);
self::assertSame(-125, array_sum(array_column($command->entries, 'delta_minor')));
}
public function test_transfer_rejects_net_below_one_minor_unit(): void {
$this->expectException(InvalidArgumentException::class);
LedgerCommand::transfer('transfer:key', 'transfer', 1, 2, AssetType::POINTS, 10, 10);
}
public function test_transfer_rejects_negative_fee_before_arithmetic(): void {
$this->expectException(InvalidArgumentException::class);
LedgerCommand::transfer('transfer:key', 'transfer', 1, 2, AssetType::POINTS, PHP_INT_MAX, -1);
}
public function test_validation_returns_400_for_bad_operation_key(): void {
$command = new LedgerCommand('bad key', 'credit', [[
'user_id' => 1,
'asset' => 'points',
'delta_minor' => 1,
'entry_type' => 'credit',
]]);
$error = $command->validate();
self::assertInstanceOf(WP_Error::class, $error);
self::assertSame('aksrt_ledger_operation_key', $error->get_error_code());
self::assertSame(400, $error->get_error_data()['status']);
}
public function test_payload_hash_is_stable_for_metadata_key_order(): void {
$entries = [['user_id' => 1, 'asset' => 'points', 'delta_minor' => 2, 'entry_type' => 'credit']];
$first = new LedgerCommand('stable:key', 'credit', $entries, '', '', 0, ['b' => 2, 'a' => 1]);
$second = new LedgerCommand('stable:key', 'credit', $entries, '', '', 0, ['a' => 1, 'b' => 2]);
self::assertSame($first->payload_hash(), $second->payload_hash());
}
public function test_payload_hash_changes_with_business_payload(): void {
$first = new LedgerCommand('stable:key', 'credit', [[
'user_id' => 1,
'asset' => 'points',
'delta_minor' => 2,
'entry_type' => 'credit',
]]);
$second = new LedgerCommand('stable:key', 'credit', [[
'user_id' => 1,
'asset' => 'points',
'delta_minor' => 3,
'entry_type' => 'credit',
]]);
self::assertNotSame($first->payload_hash(), $second->payload_hash());
}
public function test_validation_rejects_empty_entries_and_invalid_asset(): void {
$empty = new LedgerCommand('empty:key', 'credit', []);
self::assertSame('aksrt_ledger_entries', $empty->validate()->get_error_code());
$invalid = new LedgerCommand('asset:key', 'credit', [[
'user_id' => 1,
'asset' => 'cash',
'delta_minor' => 1,
'entry_type' => 'credit',
]]);
self::assertSame('aksrt_ledger_asset', $invalid->validate()->get_error_code());
}
public function test_validation_requires_integer_non_zero_entry_values(): void {
foreach (['1', 0, PHP_INT_MIN] as $delta) {
$command = new LedgerCommand('entry:key', 'credit', [[
'user_id' => 1,
'asset' => 'points',
'delta_minor' => $delta,
'entry_type' => 'credit',
]]);
self::assertSame('aksrt_ledger_entry', $command->validate()->get_error_code());
}
}
public function test_validation_rejects_negative_actor_and_invalid_entry_type(): void {
$actor = new LedgerCommand('actor:key', 'credit', [[
'user_id' => 1,
'asset' => 'points',
'delta_minor' => 1,
'entry_type' => 'credit',
]], '', '', -1);
self::assertSame('aksrt_ledger_actor', $actor->validate()->get_error_code());
$type = new LedgerCommand('type:key', 'credit', [[
'user_id' => 1,
'asset' => 'points',
'delta_minor' => 1,
'entry_type' => 'Bad Type',
]]);
self::assertSame('aksrt_ledger_entry', $type->validate()->get_error_code());
}
}