브라우저·SEO 실측(T1~T7 매트릭스 + 봇 렌더 스윕)에서 발견된 결함을 보완한다. - 코어: 회원 일괄 상태변경 응답 메시지의 :count 플레이스홀더 미치환 수정 - 게시판·페이지: 비밀글·삭제글·미발행 첨부 썸네일이 KVE-2026-1914 게이트로 깨진 것을 한시 서명 preview URL(게이트 통과 직렬화 발급 + 유효 서명 허용)로 복구 — 무서명·변조·만료 요청 게이트는 종전과 동일 - 페이지: 발행/미발행 전환(after_publish)에도 SEO 캐시·사이트맵 갱신 구독 - 게시판: 게시판 디렉토리(전체 목록 + 내장 최근글)에 열람 권한 필터 적용 - 검색 화면: searchResults 데이터소스 auth_mode=optional 선언으로 로그인 회원 검색 범위 복구 + data-sources.md 선언 의무 규정화(coverage 등록)
210 lines
7.3 KiB
PHP
210 lines
7.3 KiB
PHP
<?php
|
|
|
|
namespace Modules\Sirsoft\Board\Tests\Feature\User;
|
|
|
|
// ModuleTestCase를 수동으로 require (autoload 전에 로드 필요)
|
|
require_once __DIR__.'/../../ModuleTestCase.php';
|
|
|
|
use App\Http\Middleware\PermissionMiddleware;
|
|
use App\Models\Permission;
|
|
use App\Models\Role;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Modules\Sirsoft\Board\Models\Board;
|
|
use Modules\Sirsoft\Board\Tests\ModuleTestCase;
|
|
|
|
/**
|
|
* 사용자 게시판 접근 테스트
|
|
*
|
|
* 사용자(비로그인 포함) 페이지에서 게시판 목록 및 상세 조회 테스트
|
|
*/
|
|
class BoardAccessTest extends ModuleTestCase
|
|
{
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
// DDL implicit commit으로 커밋된 게시판 비활성화 (데이터 격리 보장)
|
|
DB::table('boards')->update(['is_active' => false]);
|
|
// 이 테스트 클래스에서 사용하는 슬러그 정리 (커밋된 경우 대비)
|
|
DB::table('boards')->whereIn('slug', ['active-1', 'active-2', 'inactive', 'test-board', 'inactive-board', 'non-existent'])->delete();
|
|
}
|
|
|
|
/**
|
|
* 비로그인 사용자도 활성화된 게시판 목록 조회 가능
|
|
*/
|
|
public function test_guest_can_access_active_boards_list(): void
|
|
{
|
|
// Given: 활성화된 게시판 2개, 비활성화된 게시판 1개 생성
|
|
Board::factory()->create([
|
|
'slug' => 'active-1',
|
|
'is_active' => true,
|
|
]);
|
|
Board::factory()->create([
|
|
'slug' => 'active-2',
|
|
'is_active' => true,
|
|
]);
|
|
Board::factory()->create([
|
|
'slug' => 'inactive',
|
|
'is_active' => false,
|
|
]);
|
|
|
|
// 디렉토리는 호출자 열람 권한 필터를 적용하므로 게스트 읽기 권한을 부여한다
|
|
$this->grantGuestRead('active-1');
|
|
$this->grantGuestRead('active-2');
|
|
$this->grantGuestRead('inactive');
|
|
|
|
// When: 비로그인 상태에서 게시판 목록 API 호출
|
|
$response = $this->getJson('/api/modules/sirsoft-board/boards');
|
|
|
|
// Then: 응답 성공, 활성화된 게시판만 2개 반환
|
|
$response->assertStatus(200);
|
|
$response->assertJsonStructure([
|
|
'success',
|
|
'message',
|
|
'data' => [
|
|
'*' => [
|
|
'id',
|
|
'name',
|
|
'slug',
|
|
],
|
|
],
|
|
]);
|
|
|
|
// 데이터가 배열 형태로 반환되는지 확인
|
|
$data = $response->json('data');
|
|
$this->assertIsArray($data);
|
|
$this->assertCount(2, $data);
|
|
|
|
// 활성화된 게시판만 반환되는지 확인
|
|
$slugs = collect($data)->pluck('slug')->toArray();
|
|
$this->assertContains('active-1', $slugs);
|
|
$this->assertContains('active-2', $slugs);
|
|
$this->assertNotContains('inactive', $slugs);
|
|
|
|
// 비활성화된 게시판은 포함되지 않아야 함 (slug 기반 확인)
|
|
$this->assertCount(2, $slugs, '활성화된 게시판만 반환되어야 합니다');
|
|
}
|
|
|
|
/**
|
|
* 비활성화된 게시판은 목록에 포함되지 않음
|
|
*/
|
|
public function test_inactive_boards_not_included_in_list(): void
|
|
{
|
|
// Given: 비활성화된 게시판 3개, 활성화된 게시판 1개 생성
|
|
$inactiveBoards = Board::factory()->count(3)->create(['is_active' => false]);
|
|
$activeBoard = Board::factory()->create(['is_active' => true]);
|
|
|
|
// 디렉토리는 호출자 열람 권한 필터를 적용하므로 게스트 읽기 권한을 부여한다
|
|
foreach ($inactiveBoards as $b) {
|
|
$this->grantGuestRead($b->slug);
|
|
}
|
|
$this->grantGuestRead($activeBoard->slug);
|
|
|
|
// When: 게시판 목록 조회
|
|
$response = $this->getJson('/api/modules/sirsoft-board/boards');
|
|
|
|
// Then: 활성화된 게시판 1개만 반환
|
|
$response->assertStatus(200);
|
|
$this->assertCount(1, $response->json('data'));
|
|
}
|
|
|
|
/**
|
|
* 활성화된 게시판 상세 조회 가능
|
|
*/
|
|
public function test_guest_can_access_active_board_detail(): void
|
|
{
|
|
// Given: 활성화된 게시판 생성
|
|
$board = Board::factory()->create([
|
|
'slug' => 'test-board',
|
|
'name' => ['ko' => '테스트 게시판', 'en' => 'Test Board'],
|
|
'is_active' => true,
|
|
]);
|
|
|
|
// When: 게시판 상세 조회
|
|
$response = $this->getJson('/api/modules/sirsoft-board/boards/test-board');
|
|
|
|
// Then: 성공 응답, 게시판 정보 반환
|
|
$response->assertStatus(200);
|
|
$response->assertJson([
|
|
'success' => true,
|
|
'data' => [
|
|
'slug' => 'test-board',
|
|
'is_active' => true,
|
|
],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 비활성화된 게시판 상세 조회 불가 (404)
|
|
*/
|
|
public function test_guest_cannot_access_inactive_board_detail(): void
|
|
{
|
|
// Given: 비활성화된 게시판 생성
|
|
Board::factory()->create([
|
|
'slug' => 'inactive-board',
|
|
'is_active' => false,
|
|
]);
|
|
|
|
// When: 비활성화된 게시판 상세 조회 시도
|
|
$response = $this->getJson('/api/modules/sirsoft-board/boards/inactive-board');
|
|
|
|
// Then: 404 응답
|
|
$response->assertStatus(404);
|
|
}
|
|
|
|
/**
|
|
* 존재하지 않는 게시판 조회 시 404
|
|
*/
|
|
public function test_accessing_non_existent_board_returns_404(): void
|
|
{
|
|
// When: 존재하지 않는 게시판 조회
|
|
$response = $this->getJson('/api/modules/sirsoft-board/boards/non-existent');
|
|
|
|
// Then: 404 응답
|
|
$response->assertStatus(404);
|
|
}
|
|
|
|
/**
|
|
* 게시판 목록 응답에 페이지네이션 정보가 포함되지 않음 (단순 배열)
|
|
*/
|
|
public function test_board_list_response_does_not_include_pagination(): void
|
|
{
|
|
// Given: 게시판 여러 개 생성 (디렉토리 열람 권한 필터 대응 — 게스트 읽기 권한 부여)
|
|
$boards = Board::factory()->count(5)->create(['is_active' => true]);
|
|
foreach ($boards as $b) {
|
|
$this->grantGuestRead($b->slug);
|
|
}
|
|
|
|
// When: 게시판 목록 조회
|
|
$response = $this->getJson('/api/modules/sirsoft-board/boards');
|
|
|
|
// Then: 페이지네이션 정보 없이 순수 배열만 반환
|
|
$response->assertStatus(200);
|
|
$this->assertArrayNotHasKey('pagination', $response->json('data'));
|
|
$this->assertIsArray($response->json('data'));
|
|
}
|
|
|
|
/**
|
|
* guest 역할에 게시판 읽기 권한을 부여합니다.
|
|
*
|
|
* 디렉토리(전체 게시판 목록)는 호출자 열람 권한 필터를 적용하므로,
|
|
* 게스트 시점 검증용 게시판에는 명시적으로 읽기 권한을 부여한다
|
|
* (PublicRecentPostsVisibilityTest 와 동일 관례).
|
|
*
|
|
* @param string $slug 게시판 슬러그
|
|
*/
|
|
private function grantGuestRead(string $slug): void
|
|
{
|
|
$role = Role::where('identifier', 'guest')->first();
|
|
if (! $role) {
|
|
return;
|
|
}
|
|
|
|
$perm = Permission::firstOrCreate(
|
|
['identifier' => "sirsoft-board.{$slug}.posts.read"],
|
|
['name' => ['ko' => 'read', 'en' => 'read'], 'type' => 'user']
|
|
);
|
|
$role->permissions()->syncWithoutDetaching([$perm->id]);
|
|
PermissionMiddleware::clearGuestRoleCache();
|
|
}
|
|
}
|