510 lines
20 KiB
PHP
510 lines
20 KiB
PHP
<?php
|
|
|
|
namespace Modules\Sirsoft\Board\Tests\Feature\User;
|
|
|
|
// 테스트 베이스 클래스 수동 require (autoload 전에 로드 필요)
|
|
require_once __DIR__.'/../../ModuleTestCase.php';
|
|
|
|
use App\Models\Permission;
|
|
use App\Models\Role;
|
|
use App\Models\User;
|
|
use Modules\Sirsoft\Board\Tests\BoardTestCase;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
|
|
/**
|
|
* 게시판/게시글/댓글 API 응답의 abilities 키 구조 테스트
|
|
*
|
|
* BoardResource는 user_abilities (can_* 형식), PostResource/CommentResource는
|
|
* abilities (can_* 형식)를 반환해야 합니다.
|
|
* 이전 underscore 형식(posts_write, manager 등)이 존재하지 않는지 검증합니다.
|
|
*/
|
|
class BoardUserAbilitiesTest extends BoardTestCase
|
|
{
|
|
private User $adminUser;
|
|
|
|
private User $regularUser;
|
|
|
|
private User $postAuthor;
|
|
|
|
/**
|
|
* 테스트 게시판 slug
|
|
*/
|
|
protected function getTestBoardSlug(): string
|
|
{
|
|
return 'user-abilities';
|
|
}
|
|
|
|
/**
|
|
* 기본 게시판 속성
|
|
*/
|
|
protected function getDefaultBoardAttributes(string $slug): array
|
|
{
|
|
return [
|
|
'slug' => $slug,
|
|
'name' => ['ko' => '사용자 권한 테스트 게시판', 'en' => 'User Abilities Test Board'],
|
|
'is_active' => true,
|
|
'use_comment' => true,
|
|
'secret_mode' => 'disabled',
|
|
'blocked_keywords' => [],
|
|
];
|
|
}
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
// 테스트 사용자 생성
|
|
$this->createTestUsers();
|
|
|
|
// 권한 설정
|
|
$this->setupPermissions();
|
|
}
|
|
|
|
/**
|
|
* 테스트 사용자 생성
|
|
*/
|
|
private function createTestUsers(): void
|
|
{
|
|
$this->adminUser = User::factory()->create();
|
|
$this->regularUser = User::factory()->create();
|
|
$this->postAuthor = User::factory()->create();
|
|
}
|
|
|
|
/**
|
|
* 권한 설정
|
|
*/
|
|
private function setupPermissions(): void
|
|
{
|
|
$slug = $this->board->slug;
|
|
|
|
// Admin 권한 생성 및 할당
|
|
$adminPermissions = [];
|
|
$adminPermissionDefs = [
|
|
'admin.posts.read' => ['ko' => '관리자 게시글 조회', 'en' => 'Admin Posts Read'],
|
|
'admin.posts.write' => ['ko' => '관리자 게시글 작성', 'en' => 'Admin Posts Write'],
|
|
'admin.comments.read' => ['ko' => '관리자 댓글 조회', 'en' => 'Admin Comments Read'],
|
|
'admin.comments.write' => ['ko' => '관리자 댓글 작성', 'en' => 'Admin Comments Write'],
|
|
'admin.manage' => ['ko' => '관리자 관리', 'en' => 'Admin Manage'],
|
|
];
|
|
|
|
foreach ($adminPermissionDefs as $action => $name) {
|
|
$permission = Permission::firstOrCreate(
|
|
['identifier' => "sirsoft-board.{$slug}.{$action}"],
|
|
[
|
|
'name' => $name,
|
|
'slug' => "sirsoft-board.{$slug}.{$action}",
|
|
'type' => 'admin',
|
|
]
|
|
);
|
|
$adminPermissions[] = $permission->id;
|
|
}
|
|
|
|
$adminRole = Role::firstOrCreate(
|
|
['identifier' => "{$slug}-admin"],
|
|
['name' => ['ko' => '게시판 관리자', 'en' => 'Board Admin']]
|
|
);
|
|
|
|
$adminRole->permissions()->syncWithoutDetaching($adminPermissions);
|
|
$this->adminUser->roles()->attach($adminRole->id);
|
|
|
|
// User 권한 생성 및 할당
|
|
$userPermissions = [];
|
|
$userPermissionDefs = [
|
|
'posts.read' => ['ko' => '게시글 조회', 'en' => 'Read Posts'],
|
|
'posts.write' => ['ko' => '게시글 작성', 'en' => 'Write Posts'],
|
|
'comments.read' => ['ko' => '댓글 조회', 'en' => 'Read Comments'],
|
|
'comments.write' => ['ko' => '댓글 작성', 'en' => 'Write Comments'],
|
|
];
|
|
|
|
foreach ($userPermissionDefs as $action => $name) {
|
|
$permission = Permission::firstOrCreate(
|
|
['identifier' => "sirsoft-board.{$slug}.{$action}"],
|
|
[
|
|
'name' => $name,
|
|
'slug' => "sirsoft-board.{$slug}.{$action}",
|
|
'type' => 'user',
|
|
]
|
|
);
|
|
$userPermissions[] = $permission->id;
|
|
}
|
|
|
|
$userRole = Role::firstOrCreate(
|
|
['identifier' => "{$slug}-user"],
|
|
['name' => ['ko' => '일반 사용자', 'en' => 'Regular User']]
|
|
);
|
|
|
|
$userRole->permissions()->syncWithoutDetaching($userPermissions);
|
|
$this->regularUser->roles()->attach($userRole->id);
|
|
$this->postAuthor->roles()->attach($userRole->id);
|
|
}
|
|
|
|
// ==========================================
|
|
// BoardResource: user_abilities 키 구조 테스트
|
|
// ==========================================
|
|
|
|
/**
|
|
* 게시판 상세 API가 user_abilities 키를 can_* 형식으로 반환
|
|
*/
|
|
#[Test]
|
|
public function board_detail_returns_user_abilities_with_can_keys(): void
|
|
{
|
|
// When: include_user_abilities 파라미터와 함께 게시판 상세 조회
|
|
$response = $this->actingAs($this->regularUser, 'sanctum')
|
|
->getJson("/api/modules/sirsoft-board/boards/{$this->board->slug}?include_user_abilities=1");
|
|
|
|
// Then: user_abilities 키가 존재하고 can_* 형식 키를 포함
|
|
$response->assertStatus(200);
|
|
$userAbilities = $response->json('data.user_abilities');
|
|
|
|
$this->assertNotNull($userAbilities, 'user_abilities 키가 존재해야 합니다');
|
|
$this->assertIsArray($userAbilities);
|
|
|
|
// can_* 형식 키 존재 확인
|
|
$this->assertArrayHasKey('can_read', $userAbilities);
|
|
$this->assertArrayHasKey('can_write', $userAbilities);
|
|
$this->assertArrayHasKey('can_read_comments', $userAbilities);
|
|
$this->assertArrayHasKey('can_write_comments', $userAbilities);
|
|
}
|
|
|
|
/**
|
|
* 게시판 상세 API가 user_permissions 키를 반환하지 않음 (이전 형식)
|
|
*/
|
|
#[Test]
|
|
public function board_detail_does_not_return_old_user_permissions_key(): void
|
|
{
|
|
// When
|
|
$response = $this->actingAs($this->regularUser, 'sanctum')
|
|
->getJson("/api/modules/sirsoft-board/boards/{$this->board->slug}?include_user_abilities=1");
|
|
|
|
// Then: user_permissions 키가 존재하지 않음
|
|
$response->assertStatus(200);
|
|
$data = $response->json('data');
|
|
|
|
$this->assertArrayNotHasKey('user_permissions', $data, 'user_permissions 키는 존재하지 않아야 합니다 (user_abilities로 대체됨)');
|
|
}
|
|
|
|
/**
|
|
* 게시판 user_abilities에 이전 underscore 형식 키가 존재하지 않음
|
|
*/
|
|
#[Test]
|
|
public function board_user_abilities_does_not_contain_old_underscore_keys(): void
|
|
{
|
|
// When
|
|
$response = $this->actingAs($this->regularUser, 'sanctum')
|
|
->getJson("/api/modules/sirsoft-board/boards/{$this->board->slug}?include_user_abilities=1");
|
|
|
|
// Then: 이전 underscore 형식 키 미존재
|
|
$response->assertStatus(200);
|
|
$userAbilities = $response->json('data.user_abilities');
|
|
|
|
// 이전 형식 키들이 존재하지 않는지 확인
|
|
$oldKeys = ['posts_read', 'posts_write', 'comments_read', 'comments_write', 'manager', 'admin_manage'];
|
|
foreach ($oldKeys as $oldKey) {
|
|
$this->assertArrayNotHasKey($oldKey, $userAbilities, "이전 형식 키 '{$oldKey}'가 존재하지 않아야 합니다");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* include_user_abilities 미전달 시 user_abilities가 null
|
|
*/
|
|
#[Test]
|
|
public function board_detail_returns_null_user_abilities_without_param(): void
|
|
{
|
|
// When: include_user_abilities 파라미터 없이 조회
|
|
$response = $this->actingAs($this->regularUser, 'sanctum')
|
|
->getJson("/api/modules/sirsoft-board/boards/{$this->board->slug}");
|
|
|
|
// Then: user_abilities가 null
|
|
$response->assertStatus(200);
|
|
$this->assertNull($response->json('data.user_abilities'), 'include_user_abilities 미전달 시 user_abilities는 null이어야 합니다');
|
|
}
|
|
|
|
// ==========================================
|
|
// PostResource: abilities 키 구조 테스트 (목록)
|
|
// ==========================================
|
|
|
|
/**
|
|
* 게시글 목록 API 가 컬렉션 레벨에서 abilities.can_* 형식을 반환
|
|
*
|
|
* 목록 성능 최적화로 per-post abilities 는 상세 페이지에서만 제공되고,
|
|
* 목록은 게시판 레벨 abilities 를 컬렉션 루트(data.abilities)에 한 번만 내려보낸다.
|
|
* (PostResource::toListArray — "게시판 권한은 PostCollection의 컬렉션 레벨 abilities로 제공됩니다")
|
|
*/
|
|
#[Test]
|
|
public function post_list_returns_collection_level_abilities_with_can_keys(): void
|
|
{
|
|
// Given: 게시글 생성
|
|
$this->createTestPost([
|
|
'user_id' => $this->postAuthor->id,
|
|
'title' => '테스트 게시글 1',
|
|
'content' => '내용 1',
|
|
]);
|
|
|
|
// When: 게시글 목록 조회
|
|
$response = $this->actingAs($this->regularUser, 'sanctum')
|
|
->getJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts");
|
|
|
|
// Then: 컬렉션 루트에 abilities 키가 can_* 형식으로 존재
|
|
$response->assertStatus(200);
|
|
$abilities = $response->json('data.abilities');
|
|
|
|
$this->assertIsArray($abilities, '컬렉션 레벨 abilities 가 존재해야 합니다');
|
|
$this->assertArrayHasKey('can_read', $abilities);
|
|
$this->assertArrayHasKey('can_write', $abilities);
|
|
}
|
|
|
|
/**
|
|
* 게시글 목록 컬렉션 abilities 에 이전 underscore 형식 키가 존재하지 않음
|
|
*/
|
|
#[Test]
|
|
public function post_list_collection_abilities_do_not_contain_old_underscore_keys(): void
|
|
{
|
|
// Given
|
|
$this->createTestPost([
|
|
'user_id' => $this->postAuthor->id,
|
|
'title' => '테스트 게시글',
|
|
'content' => '내용',
|
|
]);
|
|
|
|
// When
|
|
$response = $this->actingAs($this->regularUser, 'sanctum')
|
|
->getJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts");
|
|
|
|
// Then: 컬렉션 루트 abilities 확인
|
|
$response->assertStatus(200);
|
|
$abilities = $response->json('data.abilities');
|
|
$this->assertIsArray($abilities);
|
|
|
|
$oldKeys = ['posts_read', 'posts_write', 'comments_read', 'comments_write', 'manager'];
|
|
foreach ($oldKeys as $oldKey) {
|
|
$this->assertArrayNotHasKey($oldKey, $abilities, "게시글 목록 abilities 에 이전 형식 키 '{$oldKey}' 가 존재하지 않아야 합니다");
|
|
}
|
|
}
|
|
|
|
// ==========================================
|
|
// PostResource: abilities 키 구조 테스트 (상세)
|
|
// ==========================================
|
|
|
|
/**
|
|
* 게시글 상세 API가 abilities.can_manage, abilities.can_write_comments 반환
|
|
*/
|
|
#[Test]
|
|
public function post_detail_returns_abilities_with_can_manage_and_can_write_comments(): void
|
|
{
|
|
// Given
|
|
$postId = $this->createTestPost([
|
|
'user_id' => $this->postAuthor->id,
|
|
'title' => '상세 테스트 게시글',
|
|
'content' => '상세 내용',
|
|
]);
|
|
|
|
// When: 게시글 상세 조회
|
|
$response = $this->actingAs($this->regularUser, 'sanctum')
|
|
->getJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$postId}");
|
|
|
|
// Then: abilities에 can_manage, can_write_comments 키 존재
|
|
$response->assertStatus(200);
|
|
$abilities = $response->json('data.abilities');
|
|
|
|
$this->assertNotNull($abilities, 'abilities 키가 존재해야 합니다');
|
|
$this->assertArrayHasKey('can_manage', $abilities);
|
|
$this->assertArrayHasKey('can_write_comments', $abilities);
|
|
$this->assertArrayHasKey('can_read', $abilities);
|
|
$this->assertArrayHasKey('can_write', $abilities);
|
|
$this->assertArrayHasKey('can_read_comments', $abilities);
|
|
}
|
|
|
|
/**
|
|
* 게시글 상세 abilities에 이전 underscore 형식 키가 존재하지 않음
|
|
*/
|
|
#[Test]
|
|
public function post_detail_abilities_do_not_contain_old_underscore_keys(): void
|
|
{
|
|
// Given
|
|
$postId = $this->createTestPost([
|
|
'user_id' => $this->postAuthor->id,
|
|
'title' => '테스트 게시글',
|
|
'content' => '내용',
|
|
]);
|
|
|
|
// When
|
|
$response = $this->actingAs($this->regularUser, 'sanctum')
|
|
->getJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$postId}");
|
|
|
|
// Then
|
|
$response->assertStatus(200);
|
|
$abilities = $response->json('data.abilities');
|
|
|
|
$oldKeys = ['posts_read', 'posts_write', 'comments_read', 'comments_write', 'manager', 'admin_manage'];
|
|
foreach ($oldKeys as $oldKey) {
|
|
$this->assertArrayNotHasKey($oldKey, $abilities, "게시글 상세 abilities에 이전 형식 키 '{$oldKey}'가 존재하지 않아야 합니다");
|
|
}
|
|
}
|
|
|
|
// ==========================================
|
|
// CommentResource: abilities 키 구조 테스트
|
|
// ==========================================
|
|
|
|
/**
|
|
* 댓글이 포함된 게시글 상세에서 댓글의 abilities.can_write 반환
|
|
*/
|
|
#[Test]
|
|
public function comment_in_post_detail_returns_abilities_with_can_write(): void
|
|
{
|
|
// Given: 게시글 + 댓글 생성
|
|
$postId = $this->createTestPost([
|
|
'user_id' => $this->postAuthor->id,
|
|
'title' => '댓글 테스트 게시글',
|
|
'content' => '내용',
|
|
]);
|
|
|
|
$this->createTestComment($postId, [
|
|
'user_id' => $this->regularUser->id,
|
|
'author_name' => $this->regularUser->name,
|
|
'content' => '테스트 댓글입니다.',
|
|
]);
|
|
|
|
// When: 게시글 상세 조회 (댓글 포함)
|
|
$response = $this->actingAs($this->regularUser, 'sanctum')
|
|
->getJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$postId}");
|
|
|
|
// Then: 댓글에 abilities 키가 존재하고 can_write 포함
|
|
$response->assertStatus(200);
|
|
$comments = $response->json('data.comments');
|
|
|
|
$this->assertNotEmpty($comments, '댓글이 1개 이상 존재해야 합니다');
|
|
|
|
$firstComment = $comments[0];
|
|
$this->assertArrayHasKey('abilities', $firstComment, '댓글에 abilities 키가 존재해야 합니다');
|
|
$this->assertArrayHasKey('can_write', $firstComment['abilities']);
|
|
}
|
|
|
|
/**
|
|
* 댓글 abilities에 이전 underscore 형식 키가 존재하지 않음
|
|
*/
|
|
#[Test]
|
|
public function comment_abilities_do_not_contain_old_underscore_keys(): void
|
|
{
|
|
// Given
|
|
$postId = $this->createTestPost([
|
|
'user_id' => $this->postAuthor->id,
|
|
'title' => '댓글 테스트 게시글',
|
|
'content' => '내용',
|
|
]);
|
|
|
|
$this->createTestComment($postId, [
|
|
'user_id' => $this->regularUser->id,
|
|
'author_name' => $this->regularUser->name,
|
|
'content' => '테스트 댓글입니다.',
|
|
]);
|
|
|
|
// When
|
|
$response = $this->actingAs($this->regularUser, 'sanctum')
|
|
->getJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$postId}");
|
|
|
|
// Then
|
|
$response->assertStatus(200);
|
|
$comments = $response->json('data.comments');
|
|
$this->assertNotEmpty($comments);
|
|
|
|
$commentAbilities = $comments[0]['abilities'];
|
|
$oldKeys = ['comments_write', 'comments_read', 'manager'];
|
|
foreach ($oldKeys as $oldKey) {
|
|
$this->assertArrayNotHasKey($oldKey, $commentAbilities, "댓글 abilities에 이전 형식 키 '{$oldKey}'가 존재하지 않아야 합니다");
|
|
}
|
|
}
|
|
|
|
// ==========================================
|
|
// Admin 컨텍스트 abilities 키 구조 테스트
|
|
// ==========================================
|
|
|
|
/**
|
|
* Admin 게시글 상세에서도 abilities가 can_* 형식으로 반환
|
|
*/
|
|
#[Test]
|
|
public function admin_post_detail_returns_abilities_with_can_keys(): void
|
|
{
|
|
// Given
|
|
$postId = $this->createTestPost([
|
|
'user_id' => $this->postAuthor->id,
|
|
'title' => 'Admin 테스트 게시글',
|
|
'content' => '내용',
|
|
]);
|
|
|
|
// When: Admin API로 조회
|
|
$response = $this->actingAs($this->adminUser, 'sanctum')
|
|
->getJson("/api/modules/sirsoft-board/admin/board/{$this->board->slug}/posts/{$postId}");
|
|
|
|
// Then: abilities에 can_* 형식 키 존재
|
|
$response->assertStatus(200);
|
|
$abilities = $response->json('data.abilities');
|
|
|
|
$this->assertNotNull($abilities);
|
|
$this->assertArrayHasKey('can_read', $abilities);
|
|
$this->assertArrayHasKey('can_write', $abilities);
|
|
$this->assertArrayHasKey('can_manage', $abilities);
|
|
|
|
// 이전 형식 키 미존재
|
|
$this->assertArrayNotHasKey('admin_posts_read', $abilities);
|
|
$this->assertArrayNotHasKey('admin_posts_write', $abilities);
|
|
$this->assertArrayNotHasKey('admin_manage', $abilities);
|
|
}
|
|
|
|
// ==========================================
|
|
// 권한 값 정확성 테스트
|
|
// ==========================================
|
|
|
|
/**
|
|
* 권한을 가진 사용자의 abilities 값이 true
|
|
*/
|
|
#[Test]
|
|
public function user_with_permissions_has_true_abilities(): void
|
|
{
|
|
// Given
|
|
$postId = $this->createTestPost([
|
|
'user_id' => $this->postAuthor->id,
|
|
'title' => '권한 테스트 게시글',
|
|
'content' => '내용',
|
|
]);
|
|
|
|
// When: 권한을 가진 사용자가 조회
|
|
$response = $this->actingAs($this->regularUser, 'sanctum')
|
|
->getJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$postId}");
|
|
|
|
// Then: 해당 권한 값이 true
|
|
$response->assertStatus(200);
|
|
$abilities = $response->json('data.abilities');
|
|
|
|
$this->assertTrue($abilities['can_read'], 'posts.read 권한 보유자의 can_read는 true');
|
|
$this->assertTrue($abilities['can_write'], 'posts.write 권한 보유자의 can_write는 true');
|
|
$this->assertTrue($abilities['can_read_comments'], 'comments.read 권한 보유자의 can_read_comments는 true');
|
|
$this->assertTrue($abilities['can_write_comments'], 'comments.write 권한 보유자의 can_write_comments는 true');
|
|
}
|
|
|
|
/**
|
|
* 비회원의 abilities 값이 기본 권한에 따라 정확히 설정됨
|
|
*/
|
|
#[Test]
|
|
public function guest_user_abilities_reflect_default_permissions(): void
|
|
{
|
|
// Given: 게시글 생성
|
|
$postId = $this->createTestPost([
|
|
'user_id' => $this->postAuthor->id,
|
|
'title' => '비회원 테스트 게시글',
|
|
'content' => '내용',
|
|
]);
|
|
|
|
// When: 비로그인 상태로 조회
|
|
$response = $this->getJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$postId}");
|
|
|
|
// Then: guest에 posts.read, posts.write 있음 / comments.read, comments.write 없음
|
|
$response->assertStatus(200);
|
|
$abilities = $response->json('data.abilities');
|
|
|
|
$this->assertTrue($abilities['can_read'], 'guest에 posts.read 있으므로 can_read는 true');
|
|
$this->assertTrue($abilities['can_write'], 'guest에 posts.write 있으므로 can_write는 true');
|
|
$this->assertFalse($abilities['can_read_comments'], 'guest에 comments.read 없으므로 can_read_comments는 false');
|
|
$this->assertFalse($abilities['can_write_comments'], 'guest에 comments.write 없으므로 can_write_comments는 false');
|
|
}
|
|
}
|