优化提醒
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:聊天室快捷任命控制器测试
|
||||
*
|
||||
* 验证聊天室用户名片中的任命/撤销操作会给目标用户写入带右下角提示的私聊消息。
|
||||
*/
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Jobs\SaveMessageJob;
|
||||
use App\Models\Department;
|
||||
use App\Models\Position;
|
||||
use App\Models\Room;
|
||||
use App\Models\User;
|
||||
use App\Models\UserPosition;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* 聊天室快捷任命控制器测试类。
|
||||
*/
|
||||
class ChatAppointmentControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* 每个测试前清空 Redis,避免跨用例的聊天室消息污染断言。
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Redis::flushall();
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试任命职务后会给目标用户写入带 toast 的私聊提示。
|
||||
*/
|
||||
public function test_appoint_pushes_private_toast_notification_to_target(): void
|
||||
{
|
||||
Queue::fake();
|
||||
|
||||
[$admin, $target, $room, $position] = $this->createAppointmentActors();
|
||||
|
||||
$response = $this->actingAs($admin)->postJson(route('chat.appoint.appoint'), [
|
||||
'username' => $target->username,
|
||||
'position_id' => $position->id,
|
||||
'remark' => '聊天室快速任命',
|
||||
'room_id' => $room->id,
|
||||
]);
|
||||
|
||||
$response->assertOk()->assertJson([
|
||||
'status' => 'success',
|
||||
'message' => "已成功将【{$target->username}】任命为【{$position->name}】。",
|
||||
]);
|
||||
|
||||
$privateMessage = $this->findPrivateSystemMessage($room->id, $target->username, '已任命你为');
|
||||
|
||||
$this->assertNotNull($privateMessage);
|
||||
$this->assertSame('✨ 职务任命通知', $privateMessage['toast_notification']['title'] ?? null);
|
||||
$this->assertSame('✨', $privateMessage['toast_notification']['icon'] ?? null);
|
||||
$this->assertSame('#a855f7', $privateMessage['toast_notification']['color'] ?? null);
|
||||
|
||||
Queue::assertPushed(SaveMessageJob::class, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试撤销职务后会给目标用户写入带 toast 的私聊提示。
|
||||
*/
|
||||
public function test_revoke_pushes_private_toast_notification_to_target(): void
|
||||
{
|
||||
Queue::fake();
|
||||
|
||||
[$admin, $target, $room, $position] = $this->createAppointmentActors();
|
||||
|
||||
UserPosition::create([
|
||||
'user_id' => $target->id,
|
||||
'position_id' => $position->id,
|
||||
'appointed_by_user_id' => $admin->id,
|
||||
'appointed_at' => now()->subDay(),
|
||||
'remark' => '已有职务',
|
||||
'is_active' => true,
|
||||
]);
|
||||
$target->update(['user_level' => $position->level]);
|
||||
|
||||
$response = $this->actingAs($admin)->postJson(route('chat.appoint.revoke'), [
|
||||
'username' => $target->username,
|
||||
'remark' => '聊天室快速撤销',
|
||||
'room_id' => $room->id,
|
||||
]);
|
||||
|
||||
$response->assertOk()->assertJson([
|
||||
'status' => 'success',
|
||||
]);
|
||||
|
||||
$privateMessage = $this->findPrivateSystemMessage($room->id, $target->username, '已撤销你的');
|
||||
|
||||
$this->assertNotNull($privateMessage);
|
||||
$this->assertSame('📋 职务变动通知', $privateMessage['toast_notification']['title'] ?? null);
|
||||
$this->assertSame('📋', $privateMessage['toast_notification']['icon'] ?? null);
|
||||
$this->assertSame('#6b7280', $privateMessage['toast_notification']['color'] ?? null);
|
||||
|
||||
Queue::assertPushed(SaveMessageJob::class, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建任命测试共用的操作者、目标用户、房间与职务。
|
||||
*
|
||||
* @return array{0: User, 1: User, 2: Room, 3: Position}
|
||||
*/
|
||||
private function createAppointmentActors(): array
|
||||
{
|
||||
$admin = User::factory()->create([
|
||||
'id' => 1,
|
||||
'user_level' => 100,
|
||||
]);
|
||||
$target = User::factory()->create([
|
||||
'user_level' => 1,
|
||||
]);
|
||||
$room = Room::create([
|
||||
'room_name' => '职务操作房',
|
||||
]);
|
||||
$department = Department::create([
|
||||
'name' => '办公厅',
|
||||
'rank' => 90,
|
||||
]);
|
||||
$position = Position::create([
|
||||
'department_id' => $department->id,
|
||||
'name' => '巡查员',
|
||||
'icon' => '✨',
|
||||
'rank' => 50,
|
||||
'level' => 20,
|
||||
]);
|
||||
|
||||
return [$admin, $target, $room, $position];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从房间消息缓存中定位目标用户收到的系统私聊提示。
|
||||
*
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
private function findPrivateSystemMessage(int $roomId, string $targetUsername, string $needle): ?array
|
||||
{
|
||||
$messages = Redis::lrange("room:{$roomId}:messages", 0, -1);
|
||||
|
||||
return collect($messages)
|
||||
->map(fn (string $item) => json_decode($item, true))
|
||||
->first(fn (array $item) => ($item['from_user'] ?? null) === '系统'
|
||||
&& ($item['to_user'] ?? null) === $targetUsername
|
||||
&& str_contains((string) ($item['content'] ?? ''), $needle));
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,15 @@ class AdminCommandControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* 每个测试前清空 Redis,避免跨用例的聊天室消息污染断言。
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Redis::flushall();
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试站长可以触发全部新增全屏特效。
|
||||
*/
|
||||
@@ -98,4 +107,158 @@ class AdminCommandControllerTest extends TestCase
|
||||
// 奖励公告和接收者私信都应进入异步落库队列。
|
||||
Queue::assertPushed(SaveMessageJob::class, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试警告操作会给目标用户写入带 toast 的私聊提示。
|
||||
*/
|
||||
public function test_warn_message_contains_toast_notification_for_receiver(): void
|
||||
{
|
||||
Queue::fake();
|
||||
[$admin, $target, $room] = $this->createAdminCommandActors();
|
||||
|
||||
$response = $this->actingAs($admin)->postJson(route('command.warn'), [
|
||||
'username' => $target->username,
|
||||
'room_id' => $room->id,
|
||||
'reason' => '请注意发言尺度',
|
||||
]);
|
||||
|
||||
$response->assertOk()->assertJson([
|
||||
'status' => 'success',
|
||||
'message' => "已警告 {$target->username}",
|
||||
]);
|
||||
|
||||
$privateMessage = $this->findPrivateSystemMessage($room->id, $target->username, '管理员 <b>'.$admin->username.'</b> 警告了你');
|
||||
|
||||
$this->assertNotNull($privateMessage);
|
||||
$this->assertSame('⚠️ 收到警告', $privateMessage['toast_notification']['title'] ?? null);
|
||||
$this->assertSame('⚠️', $privateMessage['toast_notification']['icon'] ?? null);
|
||||
$this->assertSame('#f59e0b', $privateMessage['toast_notification']['color'] ?? null);
|
||||
Queue::assertPushed(SaveMessageJob::class, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试禁言操作会给目标用户写入带 toast 的私聊提示。
|
||||
*/
|
||||
public function test_mute_message_contains_toast_notification_for_receiver(): void
|
||||
{
|
||||
Queue::fake();
|
||||
[$admin, $target, $room] = $this->createAdminCommandActors();
|
||||
|
||||
$response = $this->actingAs($admin)->postJson(route('command.mute'), [
|
||||
'username' => $target->username,
|
||||
'room_id' => $room->id,
|
||||
'duration' => 15,
|
||||
]);
|
||||
|
||||
$response->assertOk()->assertJson([
|
||||
'status' => 'success',
|
||||
'message' => "已禁言 {$target->username} 15 分钟",
|
||||
]);
|
||||
|
||||
$this->assertTrue((bool) Redis::exists("mute:{$room->id}:{$target->username}"));
|
||||
|
||||
$privateMessage = $this->findPrivateSystemMessage($room->id, $target->username, '已将你禁言 15 分钟');
|
||||
|
||||
$this->assertNotNull($privateMessage);
|
||||
$this->assertSame('🔇 已被禁言', $privateMessage['toast_notification']['title'] ?? null);
|
||||
$this->assertSame('🔇', $privateMessage['toast_notification']['icon'] ?? null);
|
||||
$this->assertSame('#6366f1', $privateMessage['toast_notification']['color'] ?? null);
|
||||
Queue::assertPushed(SaveMessageJob::class, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试踢出操作会给目标用户写入带 toast 的私聊提示。
|
||||
*/
|
||||
public function test_kick_message_contains_toast_notification_for_receiver(): void
|
||||
{
|
||||
Queue::fake();
|
||||
[$admin, $target, $room] = $this->createAdminCommandActors();
|
||||
Redis::hset("room:{$room->id}:users", $target->username, json_encode(['user_id' => $target->id], JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$response = $this->actingAs($admin)->postJson(route('command.kick'), [
|
||||
'username' => $target->username,
|
||||
'room_id' => $room->id,
|
||||
'reason' => '刷屏',
|
||||
]);
|
||||
|
||||
$response->assertOk()->assertJson([
|
||||
'status' => 'success',
|
||||
'message' => "已踢出 {$target->username}",
|
||||
]);
|
||||
|
||||
$privateMessage = $this->findPrivateSystemMessage($room->id, $target->username, '已将你踢出聊天室');
|
||||
|
||||
$this->assertNotNull($privateMessage);
|
||||
$this->assertSame('🚫 已被踢出', $privateMessage['toast_notification']['title'] ?? null);
|
||||
$this->assertSame('🚫', $privateMessage['toast_notification']['icon'] ?? null);
|
||||
$this->assertSame('#ef4444', $privateMessage['toast_notification']['color'] ?? null);
|
||||
Queue::assertPushed(SaveMessageJob::class, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试冻结操作会给目标用户写入带 toast 的私聊提示。
|
||||
*/
|
||||
public function test_freeze_message_contains_toast_notification_for_receiver(): void
|
||||
{
|
||||
Queue::fake();
|
||||
[$admin, $target, $room] = $this->createAdminCommandActors();
|
||||
|
||||
$response = $this->actingAs($admin)->postJson(route('command.freeze'), [
|
||||
'username' => $target->username,
|
||||
'room_id' => $room->id,
|
||||
'reason' => '严重违规',
|
||||
]);
|
||||
|
||||
$response->assertOk()->assertJson([
|
||||
'status' => 'success',
|
||||
'message' => "已冻结 {$target->username} 的账号",
|
||||
]);
|
||||
|
||||
$this->assertSame(-1, (int) $target->fresh()->user_level);
|
||||
|
||||
$privateMessage = $this->findPrivateSystemMessage($room->id, $target->username, '已冻结你的账号');
|
||||
|
||||
$this->assertNotNull($privateMessage);
|
||||
$this->assertSame('🧊 账号已冻结', $privateMessage['toast_notification']['title'] ?? null);
|
||||
$this->assertSame('🧊', $privateMessage['toast_notification']['icon'] ?? null);
|
||||
$this->assertSame('#3b82f6', $privateMessage['toast_notification']['color'] ?? null);
|
||||
Queue::assertPushed(SaveMessageJob::class, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建管理员命令测试共用的操作者、目标用户和房间。
|
||||
*
|
||||
* @return array{0: User, 1: User, 2: Room}
|
||||
*/
|
||||
private function createAdminCommandActors(): array
|
||||
{
|
||||
$admin = User::factory()->create([
|
||||
'id' => 1,
|
||||
'user_level' => 100,
|
||||
]);
|
||||
$target = User::factory()->create([
|
||||
'user_level' => 1,
|
||||
]);
|
||||
$room = Room::create([
|
||||
'room_name' => '管理操作房',
|
||||
]);
|
||||
|
||||
return [$admin, $target, $room];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从房间消息缓存中定位目标用户收到的系统私聊提示。
|
||||
*
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
private function findPrivateSystemMessage(int $roomId, string $targetUsername, string $needle): ?array
|
||||
{
|
||||
$messages = Redis::lrange("room:{$roomId}:messages", 0, -1);
|
||||
|
||||
return collect($messages)
|
||||
->map(fn (string $item) => json_decode($item, true))
|
||||
->first(fn (array $item) => ($item['from_user'] ?? null) === '系统'
|
||||
&& ($item['to_user'] ?? null) === $targetUsername
|
||||
&& str_contains((string) ($item['content'] ?? ''), $needle));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user