加固房间准入与消息广播边界
This commit is contained in:
@@ -1,12 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:聊天室控制器功能测试
|
||||
*
|
||||
* 覆盖进房、发言、图片消息、权限限制与广播范围等关键聊天流程。
|
||||
*/
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Events\MessageSent;
|
||||
use App\Models\Room;
|
||||
use App\Models\User;
|
||||
use Illuminate\Broadcasting\PresenceChannel;
|
||||
use Illuminate\Broadcasting\PrivateChannel;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Broadcast;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
@@ -14,8 +24,7 @@ use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* 聊天室控制器功能测试
|
||||
* 覆盖进房、发言、图片消息与退房等关键聊天流程。
|
||||
* 类功能:验证聊天室核心控制器的关键行为。
|
||||
*/
|
||||
class ChatControllerTest extends TestCase
|
||||
{
|
||||
@@ -33,7 +42,7 @@ class ChatControllerTest extends TestCase
|
||||
/**
|
||||
* 测试用户可以正常进入聊天室页面。
|
||||
*/
|
||||
public function test_can_view_room()
|
||||
public function test_can_view_room(): void
|
||||
{
|
||||
$room = Room::create(['room_name' => 'testroom']);
|
||||
$user = User::factory()->create();
|
||||
@@ -47,6 +56,70 @@ class ChatControllerTest extends TestCase
|
||||
$this->assertEquals(1, Redis::hexists("room:{$room->id}:users", $user->username));
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试关闭房间会拒绝普通用户直接进入。
|
||||
*/
|
||||
public function test_cannot_view_closed_room_without_bypass_permission(): void
|
||||
{
|
||||
$room = Room::create([
|
||||
'room_name' => 'closed',
|
||||
'door_open' => false,
|
||||
]);
|
||||
$user = User::factory()->create([
|
||||
'user_level' => 1,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->get(route('chat.room', $room->id));
|
||||
|
||||
$response->assertForbidden();
|
||||
$this->assertSame(0, Redis::hexists("room:{$room->id}:users", $user->username));
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试等级不足的用户不能进入受限房间。
|
||||
*/
|
||||
public function test_cannot_view_room_when_level_is_below_permit_level(): void
|
||||
{
|
||||
$room = Room::create([
|
||||
'room_name' => 'viprm',
|
||||
'permit_level' => 10,
|
||||
'door_open' => true,
|
||||
]);
|
||||
$user = User::factory()->create([
|
||||
'user_level' => 5,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->get(route('chat.room', $room->id));
|
||||
|
||||
$response->assertForbidden();
|
||||
$this->assertSame(0, Redis::hexists("room:{$room->id}:users", $user->username));
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试只有已成功进入房间的用户才可通过 Presence 频道鉴权。
|
||||
*/
|
||||
public function test_room_presence_channel_requires_room_access_and_join_state(): void
|
||||
{
|
||||
$room = Room::create([
|
||||
'room_name' => 'guard',
|
||||
'door_open' => true,
|
||||
]);
|
||||
$user = User::factory()->create();
|
||||
$channelCallback = Broadcast::driver()->getChannels()->get('room.{roomId}');
|
||||
|
||||
$this->assertIsCallable($channelCallback);
|
||||
$this->assertFalse($channelCallback($user, (string) $room->id));
|
||||
|
||||
$this->actingAs($user)->get(route('chat.room', $room->id));
|
||||
|
||||
$authorizedPayload = $channelCallback($user, (string) $room->id);
|
||||
$this->assertIsArray($authorizedPayload);
|
||||
$this->assertSame($user->id, $authorizedPayload['id'] ?? null);
|
||||
|
||||
Redis::del("room:{$room->id}:users");
|
||||
$this->assertFalse($channelCallback($user, (string) $room->id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试主干默认聊天室页面不会渲染虚拟形象挂载点和配置。
|
||||
*/
|
||||
@@ -87,7 +160,7 @@ class ChatControllerTest extends TestCase
|
||||
/**
|
||||
* 测试用户可以发送普通文本消息。
|
||||
*/
|
||||
public function test_can_send_message()
|
||||
public function test_can_send_message(): void
|
||||
{
|
||||
$room = Room::create(['room_name' => 'test_send']);
|
||||
$user = User::factory()->create();
|
||||
@@ -100,7 +173,7 @@ class ChatControllerTest extends TestCase
|
||||
'content' => '测试消息',
|
||||
'is_secret' => false,
|
||||
'font_color' => '#000000',
|
||||
'action' => 'say',
|
||||
'action' => '微笑',
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
@@ -121,6 +194,80 @@ class ChatControllerTest extends TestCase
|
||||
$this->assertTrue($found, 'Message not found in Redis');
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试发送接口会拦截不在白名单内的危险动作值。
|
||||
*/
|
||||
public function test_send_message_rejects_invalid_action_value(): void
|
||||
{
|
||||
$room = Room::create(['room_name' => 'badact']);
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->actingAs($user)->get(route('chat.room', $room->id));
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('chat.send', $room->id), [
|
||||
'to_user' => '大家',
|
||||
'content' => '危险动作测试',
|
||||
'is_secret' => false,
|
||||
'font_color' => '#000000',
|
||||
'action' => '\"></span><img src=x onerror=alert(1)>',
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
$response->assertJsonValidationErrors('action');
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试定向消息仅广播到发送方与接收方私有频道。
|
||||
*/
|
||||
public function test_targeted_message_event_uses_private_user_channels(): void
|
||||
{
|
||||
$sender = User::factory()->create(['username' => 'sender-user']);
|
||||
$receiver = User::factory()->create(['username' => 'receiver-user']);
|
||||
|
||||
$event = new MessageSent(1, [
|
||||
'room_id' => 1,
|
||||
'from_user' => $sender->username,
|
||||
'to_user' => $receiver->username,
|
||||
'content' => '只给你看',
|
||||
'is_secret' => true,
|
||||
'font_color' => '#000000',
|
||||
'action' => '',
|
||||
'sent_at' => now()->toDateTimeString(),
|
||||
]);
|
||||
|
||||
$channels = $event->broadcastOn();
|
||||
|
||||
$this->assertCount(2, $channels);
|
||||
$this->assertContainsOnlyInstancesOf(PrivateChannel::class, $channels);
|
||||
$this->assertSame([
|
||||
'private-user.'.$sender->id,
|
||||
'private-user.'.$receiver->id,
|
||||
], array_map(fn (PrivateChannel $channel) => $channel->name, $channels));
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试公共消息仍广播到房间 Presence 频道。
|
||||
*/
|
||||
public function test_public_message_event_still_uses_room_presence_channel(): void
|
||||
{
|
||||
$event = new MessageSent(3, [
|
||||
'room_id' => 3,
|
||||
'from_user' => 'tester',
|
||||
'to_user' => '大家',
|
||||
'content' => '公开消息',
|
||||
'is_secret' => false,
|
||||
'font_color' => '#000000',
|
||||
'action' => '',
|
||||
'sent_at' => now()->toDateTimeString(),
|
||||
]);
|
||||
|
||||
$channels = $event->broadcastOn();
|
||||
|
||||
$this->assertCount(1, $channels);
|
||||
$this->assertInstanceOf(PresenceChannel::class, $channels[0]);
|
||||
$this->assertSame('presence-room.3', $channels[0]->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试文本内容为字符串 0 时仍可正常发送。
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user