dootask/tests/Feature/DialogRemovePermissionTest.php

287 lines
11 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Tests\Feature;
use App\Models\User;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogUser;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;
/**
* 群成员移出权限保护测试
*/
class DialogRemovePermissionTest extends TestCase
{
use DatabaseTransactions;
private function makeUser(string $email): User
{
$user = User::createInstance([
'email' => $email,
'userimg' => '',
'nickname' => 'TestUser_' . substr(md5($email), 0, 6),
'profession' => '',
'password' => md5('123456'),
]);
$user->save();
return $user;
}
/**
* 检查移出权限(模拟 exitGroup 中的权限检查逻辑)
*/
private function checkRemovePermission(WebSocketDialog $dialog, int $actorUserid, int $targetUserid): void
{
$member = WebSocketDialogUser::where('dialog_id', $dialog->id)
->where('userid', $targetUserid)
->first();
if (!$member) {
throw new \RuntimeException('target not member');
}
$actor = $actorUserid;
if ($actor <= 0) {
throw new \RuntimeException('只有群主或邀请人可以移出成员');
}
// 目标是群主或群管理员时的保护
$targetIsPrimaryOwner = $dialog->isPrimaryOwner($targetUserid);
$targetIsDeputyOwner = $dialog->isDeputyOwner($targetUserid);
if ($targetIsPrimaryOwner || $targetIsDeputyOwner) {
// 普通邀请人不能移出群主或群管理员
$actorIsPrimaryOwner = $dialog->isPrimaryOwner($actor);
$actorIsDeputyOwner = $dialog->isDeputyOwner($actor);
if (!$actorIsPrimaryOwner && !$actorIsDeputyOwner) {
throw new \RuntimeException('普通成员不能移出群主或群管理员');
}
// 群管理员不能移出群主或其他群管理员
if ($actorIsDeputyOwner && !$actorIsPrimaryOwner) {
throw new \RuntimeException('群管理员不能移出群主或其他群管理员');
}
}
// 普通成员:群主、群管理员、邀请人可移出
$allowedActor = $dialog->isOwner($actor) || $actor === (int)$member->inviter;
if (!$allowedActor) {
throw new \RuntimeException('只有群主、群管理员或邀请人可以移出成员');
}
}
/**
* 执行移出(权限检查通过后)
*/
private function simulateRemove(WebSocketDialog $dialog, int $actorUserid, int $targetUserid): void
{
$this->checkRemovePermission($dialog, $actorUserid, $targetUserid);
// 执行移出
WebSocketDialogUser::where('dialog_id', $dialog->id)
->where('userid', $targetUserid)
->delete();
}
/**
* 测试:普通邀请人不能移出群主
*/
public function test_inviter_cannot_remove_primary_owner()
{
$owner = $this->makeUser('owner@test.local');
$inviter = $this->makeUser('inviter@test.local');
$dialog = WebSocketDialog::createGroup('TestGroup', [$owner->userid, $inviter->userid], 'user', $owner->userid);
// inviter 邀请了 owner模拟场景
WebSocketDialogUser::where('dialog_id', $dialog->id)
->where('userid', $owner->userid)
->update(['inviter' => $inviter->userid]);
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('普通成员不能移出群主或群管理员');
$this->simulateRemove($dialog, $inviter->userid, $owner->userid);
}
/**
* 测试:普通邀请人不能移出群管理员
*/
public function test_inviter_cannot_remove_deputy_owner()
{
$owner = $this->makeUser('owner2@test.local');
$deputy = $this->makeUser('deputy2@test.local');
$inviter = $this->makeUser('inviter2@test.local');
$dialog = WebSocketDialog::createGroup('TestGroup2', [$owner->userid, $deputy->userid, $inviter->userid], 'user', $owner->userid);
// 设置群管理员
WebSocketDialogUser::where('dialog_id', $dialog->id)
->where('userid', $deputy->userid)
->update(['role' => 2]);
// inviter 邀请了 deputy模拟场景
WebSocketDialogUser::where('dialog_id', $dialog->id)
->where('userid', $deputy->userid)
->update(['inviter' => $inviter->userid]);
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('普通成员不能移出群主或群管理员');
$this->simulateRemove($dialog, $inviter->userid, $deputy->userid);
}
/**
* 测试:群管理员不能移出群主
*/
public function test_deputy_cannot_remove_primary_owner()
{
$owner = $this->makeUser('owner3@test.local');
$deputy = $this->makeUser('deputy3@test.local');
$dialog = WebSocketDialog::createGroup('TestGroup3', [$owner->userid, $deputy->userid], 'user', $owner->userid);
// 设置群管理员
WebSocketDialogUser::where('dialog_id', $dialog->id)
->where('userid', $deputy->userid)
->update(['role' => 2]);
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('群管理员不能移出群主或其他群管理员');
$this->simulateRemove($dialog, $deputy->userid, $owner->userid);
}
/**
* 测试:群管理员不能移出其他群管理员
*/
public function test_deputy_cannot_remove_other_deputy()
{
$owner = $this->makeUser('owner4@test.local');
$deputy1 = $this->makeUser('deputy4_1@test.local');
$deputy2 = $this->makeUser('deputy4_2@test.local');
$dialog = WebSocketDialog::createGroup('TestGroup4', [$owner->userid, $deputy1->userid, $deputy2->userid], 'user', $owner->userid);
// 设置两个群管理员
WebSocketDialogUser::where('dialog_id', $dialog->id)
->whereIn('userid', [$deputy1->userid, $deputy2->userid])
->update(['role' => 2]);
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('群管理员不能移出群主或其他群管理员');
$this->simulateRemove($dialog, $deputy1->userid, $deputy2->userid);
}
/**
* 测试:群管理员可以移出普通成员
*/
public function test_deputy_can_remove_regular_member()
{
$owner = $this->makeUser('owner5@test.local');
$deputy = $this->makeUser('deputy5@test.local');
$member = $this->makeUser('member5@test.local');
$dialog = WebSocketDialog::createGroup('TestGroup5', [$owner->userid, $deputy->userid, $member->userid], 'user', $owner->userid);
// 设置群管理员
WebSocketDialogUser::where('dialog_id', $dialog->id)
->where('userid', $deputy->userid)
->update(['role' => 2]);
// 应该成功
$this->simulateRemove($dialog, $deputy->userid, $member->userid);
$this->assertFalse(WebSocketDialogUser::where('dialog_id', $dialog->id)
->where('userid', $member->userid)->exists());
}
/**
* 测试:群主可以移出群管理员
*/
public function test_primary_owner_can_remove_deputy()
{
$owner = $this->makeUser('owner6@test.local');
$deputy = $this->makeUser('deputy6@test.local');
$dialog = WebSocketDialog::createGroup('TestGroup6', [$owner->userid, $deputy->userid], 'user', $owner->userid);
// 设置群管理员
WebSocketDialogUser::where('dialog_id', $dialog->id)
->where('userid', $deputy->userid)
->update(['role' => 2]);
// 应该成功
$this->simulateRemove($dialog, $owner->userid, $deputy->userid);
$this->assertFalse(WebSocketDialogUser::where('dialog_id', $dialog->id)
->where('userid', $deputy->userid)->exists());
}
/**
* 测试:普通邀请人可以移出自己邀请的普通成员
*/
public function test_inviter_can_remove_invited_regular_member()
{
$owner = $this->makeUser('owner7@test.local');
$inviter = $this->makeUser('inviter7@test.local');
$invited = $this->makeUser('invited7@test.local');
$dialog = WebSocketDialog::createGroup('TestGroup7', [$owner->userid, $inviter->userid, $invited->userid], 'user', $owner->userid);
// inviter 邀请了 invited
WebSocketDialogUser::where('dialog_id', $dialog->id)
->where('userid', $invited->userid)
->update(['inviter' => $inviter->userid]);
// 应该成功
$this->simulateRemove($dialog, $inviter->userid, $invited->userid);
$this->assertFalse(WebSocketDialogUser::where('dialog_id', $dialog->id)
->where('userid', $invited->userid)->exists());
}
/**
* 测试:非邀请人的普通成员不能移出其他普通成员
*/
public function test_regular_member_cannot_remove_other_member()
{
$owner = $this->makeUser('owner8@test.local');
$member1 = $this->makeUser('member8_1@test.local');
$member2 = $this->makeUser('member8_2@test.local');
$dialog = WebSocketDialog::createGroup('TestGroup8', [$owner->userid, $member1->userid, $member2->userid], 'user', $owner->userid);
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('只有群主、群管理员或邀请人可以移出成员');
$this->simulateRemove($dialog, $member1->userid, $member2->userid);
}
/**
* P2DB::table/stdClass 渠道的会话数据也应稳定包含 deputy_ids
*/
public function test_synthesize_data_from_db_row_includes_deputy_ids()
{
$owner = $this->makeUser('owner9@test.local');
$deputy = $this->makeUser('deputy9@test.local');
$member = $this->makeUser('member9@test.local');
$dialog = WebSocketDialog::createGroup('TestGroup9', [$owner->userid, $deputy->userid, $member->userid], 'user', $owner->userid);
WebSocketDialogUser::where('dialog_id', $dialog->id)
->where('userid', $deputy->userid)
->update(['role' => 2]);
// 模拟 getDialogList/searchDialog/getDialogBeyond 的 DB::table stdClass 数据源
$row = \DB::table('web_socket_dialog_users as u')
->select(['d.*', 'u.top_at', 'u.last_at', 'u.mark_unread', 'u.silence', 'u.hide', 'u.color', 'u.updated_at as user_at'])
->join('web_socket_dialogs as d', 'u.dialog_id', '=', 'd.id')
->where('u.dialog_id', $dialog->id)
->where('u.userid', $owner->userid)
->first();
$data = WebSocketDialog::synthesizeData($row, $owner->userid);
$this->assertArrayHasKey('deputy_ids', $data);
$this->assertContains((int)$deputy->userid, $data['deputy_ids']);
$this->assertNotContains((int)$owner->userid, $data['deputy_ids']);
$this->assertNotContains((int)$member->userid, $data['deputy_ids']);
}
}