Files
Gnuboard7/modules/_bundled/sirsoft-board/tests/Feature/User/UserPublicPostsApiTest.php
T
2026-05-11 11:29:41 +09:00

318 lines
11 KiB
PHP

<?php
namespace Modules\Sirsoft\Board\Tests\Feature\User;
// ModuleTestCase를 수동으로 require (autoload 전에 로드 필요)
require_once __DIR__.'/../../ModuleTestCase.php';
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Modules\Sirsoft\Board\Enums\PostStatus;
use Modules\Sirsoft\Board\Models\Comment;
use Modules\Sirsoft\Board\Models\Post;
use Modules\Sirsoft\Board\Tests\BoardTestCase;
/**
* 사용자 공개 게시글 API 테스트
*
* 타인의 공개 프로필에서 게시글 목록과 통계를 조회하는 API 테스트입니다.
* BoardTestCase를 통해 테스트 보드와 데이터 정리를 자동으로 처리합니다.
*
* @group board
* @group user-public-posts
*/
class UserPublicPostsApiTest extends BoardTestCase
{
/**
* 테스트용 사용자 (게시글 작성자)
*/
private User $targetUser;
/**
* 테스트 게시판 slug
*/
protected function getTestBoardSlug(): string
{
return 'user-public-posts';
}
/**
* 기본 게시판 속성
*/
protected function getDefaultBoardAttributes(string $slug): array
{
return [
'slug' => $slug,
'name' => ['ko' => '공개 게시글 테스트 게시판', 'en' => 'Public Posts Test Board'],
'is_active' => true,
'blocked_keywords' => [],
];
}
/**
* 각 테스트 전 실행
*/
protected function setUp(): void
{
parent::setUp();
// 다른 테스트 클래스에서 user_id=1로 커밋된 posts가 user_id 기반 API에 영향을 미치므로 전체 정리
// (truncateUserTables() 후 새 user는 항상 id=1을 받으므로 타 테스트 posts와 충돌)
DB::table('board_posts')->delete();
DB::table('board_comments')->delete();
// 테스트 대상 사용자 생성 (parent::setUp()에서 users 테이블 초기화됨)
$this->targetUser = User::factory()->create();
}
/**
* 각 테스트 후 실행
*/
protected function tearDown(): void
{
parent::tearDown();
}
/**
* 비인증 사용자가 다른 사용자의 공개 게시글을 조회할 수 있다.
*/
public function test_guest_can_fetch_user_public_posts(): void
{
// Given: 사용자가 게시글을 작성했을 때
$this->createPost($this->board->slug, [
'user_id' => $this->targetUser->id,
'title' => '공개 게시글',
'status' => PostStatus::Published->value,
]);
// When: 비인증 사용자가 해당 사용자의 게시글을 조회하면
$response = $this->getJson("/api/modules/sirsoft-board/users/{$this->targetUser->uuid}/posts");
// Then: 성공 응답과 함께 게시글이 반환되어야 함
$response->assertOk();
$data = $response->json('data.data');
$this->assertIsArray($data);
$this->assertCount(1, $data);
$this->assertEquals('공개 게시글', $data[0]['title']);
}
/**
* 비밀글도 사용자 프로필 게시글 목록에 포함된다 (is_secret 배지로 구분).
*
* getUserPublicPosts는 모든 게시글을 표시하며 비밀글/블라인드는 배지로 구분합니다.
*/
public function test_secret_posts_are_included_with_badge(): void
{
// Given: 공개글과 비밀글을 작성
$this->createPost($this->board->slug, [
'user_id' => $this->targetUser->id,
'title' => '공개 게시글',
'is_secret' => false,
'status' => PostStatus::Published->value,
]);
$this->createPost($this->board->slug, [
'user_id' => $this->targetUser->id,
'title' => '비밀 게시글',
'is_secret' => true,
'status' => PostStatus::Published->value,
]);
// When: 사용자 게시글을 조회하면
$response = $this->getJson("/api/modules/sirsoft-board/users/{$this->targetUser->uuid}/posts");
// Then: 모든 게시글이 반환되며 is_secret 배지로 구분
$response->assertOk();
$data = $response->json('data.data');
$this->assertCount(2, $data);
}
/**
* 블라인드 처리된 게시글도 사용자 프로필 게시글 목록에 포함된다 (status 배지로 구분).
*
* getUserPublicPosts는 모든 게시글을 표시하며 비밀글/블라인드는 배지로 구분합니다.
*/
public function test_blinded_posts_are_included_with_badge(): void
{
// Given: 공개된 게시글과 블라인드 처리된 게시글
$this->createPost($this->board->slug, [
'user_id' => $this->targetUser->id,
'title' => '발행된 게시글',
'status' => PostStatus::Published->value,
]);
$this->createPost($this->board->slug, [
'user_id' => $this->targetUser->id,
'title' => '블라인드 게시글',
'status' => PostStatus::Blinded->value,
]);
// When: 사용자 게시글을 조회하면
$response = $this->getJson("/api/modules/sirsoft-board/users/{$this->targetUser->uuid}/posts");
// Then: 모든 게시글이 반환되며 status 배지로 구분
$response->assertOk();
$data = $response->json('data.data');
$this->assertCount(2, $data);
}
/**
* 게시글/댓글 통계를 조회할 수 있다.
*/
public function test_can_fetch_user_posts_stats(): void
{
// Given: 게시글과 댓글 작성
$post = $this->createPost($this->board->slug, [
'user_id' => $this->targetUser->id,
'title' => '테스트 게시글',
'status' => PostStatus::Published->value,
]);
$this->createComment($this->board->slug, $post->id, [
'user_id' => $this->targetUser->id,
'content' => '내 댓글 1',
'status' => PostStatus::Published->value,
]);
$this->createComment($this->board->slug, $post->id, [
'user_id' => $this->targetUser->id,
'content' => '내 댓글 2',
'status' => PostStatus::Published->value,
]);
// When: 통계를 조회하면
$response = $this->getJson("/api/modules/sirsoft-board/users/{$this->targetUser->uuid}/posts/stats");
// Then: 통계 데이터가 반환되어야 함
$response->assertOk();
$data = $response->json('data');
$this->assertEquals(1, $data['posts_count']);
$this->assertEquals(2, $data['comments_count']);
}
/**
* 통계에서 블라인드 게시글/댓글은 제외된다.
*/
public function test_stats_exclude_blinded_items(): void
{
// Given: 발행된 게시글과 블라인드 처리된 게시글
$this->createPost($this->board->slug, [
'user_id' => $this->targetUser->id,
'title' => '발행된 게시글',
'status' => PostStatus::Published->value,
]);
$this->createPost($this->board->slug, [
'user_id' => $this->targetUser->id,
'title' => '블라인드 게시글',
'status' => PostStatus::Blinded->value,
]);
// When: 통계를 조회하면
$response = $this->getJson("/api/modules/sirsoft-board/users/{$this->targetUser->uuid}/posts/stats");
// Then: 발행된 게시글만 카운트되어야 함
$response->assertOk();
$data = $response->json('data');
$this->assertEquals(1, $data['posts_count']);
}
/**
* 페이지네이션이 정상 동작한다.
*/
public function test_pagination_works(): void
{
// Given: 25개의 게시글 작성
for ($i = 1; $i <= 25; $i++) {
$this->createPost($this->board->slug, [
'user_id' => $this->targetUser->id,
'title' => "게시글 {$i}",
'status' => PostStatus::Published->value,
]);
}
// When: 첫 페이지 조회
$response = $this->getJson("/api/modules/sirsoft-board/users/{$this->targetUser->uuid}/posts?page=1");
// Then: 20개의 항목과 페이지네이션 메타데이터가 있어야 함
$response->assertOk();
$this->assertCount(20, $response->json('data.data'));
$this->assertEquals(1, $response->json('data.current_page'));
$this->assertEquals(2, $response->json('data.last_page'));
$this->assertEquals(25, $response->json('data.total'));
}
/**
* per_page 파라미터로 페이지당 항목 수를 조절할 수 있다.
*/
public function test_per_page_parameter_works(): void
{
// Given: 10개의 게시글 작성
for ($i = 1; $i <= 10; $i++) {
$this->createPost($this->board->slug, [
'user_id' => $this->targetUser->id,
'title' => "게시글 {$i}",
'status' => PostStatus::Published->value,
]);
}
// When: per_page=5로 조회
$response = $this->getJson("/api/modules/sirsoft-board/users/{$this->targetUser->uuid}/posts?per_page=5");
// Then: 5개의 항목만 반환되어야 함
$response->assertOk();
$this->assertCount(5, $response->json('data.data'));
}
/**
* 존재하지 않는 사용자의 게시글 조회 시 빈 배열이 반환된다.
*/
public function test_returns_empty_for_non_existent_user(): void
{
// When: 존재하지 않는 사용자 UUID 로 조회 (라우트는 {user:uuid} 바인딩)
$nonExistentUuid = '00000000-0000-0000-0000-000000000000';
$response = $this->getJson("/api/modules/sirsoft-board/users/{$nonExistentUuid}/posts");
// Then: 모델 바인딩 실패로 404 또는 빈 배열 (optional.sanctum 경로)
// UUID 형식은 유효하지만 실제 사용자가 없으므로 ImplicitRouteBinding 이 404 반환
$response->assertStatus(404);
}
/**
* 헬퍼 메서드: 게시글 생성
*/
private function createPost(string $slug, array $attributes = []): Post
{
return Post::create(array_merge([
'board_id' => $this->board->id,
'title' => 'Test Post',
'content' => 'Test Content',
'user_id' => $this->targetUser->id,
'ip_address' => '127.0.0.1',
'is_notice' => false,
'is_secret' => false,
'status' => PostStatus::Published->value,
], $attributes));
}
/**
* 헬퍼 메서드: 댓글 생성
*/
private function createComment(string $slug, int $postId, array $attributes = []): Comment
{
return Comment::create(array_merge([
'board_id' => $this->board->id,
'post_id' => $postId,
'content' => 'Test Comment',
'user_id' => $this->targetUser->id,
'status' => PostStatus::Published->value,
], $attributes));
}
}