확장이 훅으로 등록한 카탈로그에서 고른 값은 그 확장이 사라져도 저장값으로 남는다. 그 상태에서 값만 보고 판정하면 이미 제공 불가한 항목이 사용자 화면에 그대로 노출된다. 예외도 경고도 로그도 남지 않아 관리자 화면과 나란히 보지 않으면 드러나지 않는다. - 결제수단에 지정한 PG, 현금영수증 발급사, 검색엔진 드라이버 세 축에 같은 판정을 적용. 공개 응답에서는 제거하고 관리자 응답에는 표시를 남긴다 — 운영자가 고쳐야 할 대상이라 감추면 복구 경로가 사라진다. 서버 검증도 같은 판정을 공유해 화면 우회 제출을 막는다. - 설정 단건 저장이 벌크 저장과 다른 경로를 타던 문제를 코어·이커머스·게시판에서 정리. 저장은 성공하고 파생 값만 갱신되지 않아 어느 화면에서 저장했느냐로 결과가 갈렸다. - 단건 저장의 값 형태 계약 신설. 빈 값으로 되돌릴 수 있게 하고, 타입은 defaults.json 의 기본값을 SSoT 로 삼는다. 해석 불가한 입력은 조용히 캐스팅하지 않고 거절한다. - 드라이버 셀렉트를 서버 카탈로그에 바인딩하고, 저장값이 카탈로그에 없으면 그 값을 드러내는 안내를 8곳 전수에 붙인다. 종전에는 빈 칸으로만 보여 무엇이 저장됐는지 알 수 없었다. 시나리오 매니페스트 8종을 면제 없이 신설했다. `audit:allow` 는 cross product 와 effects 검사를 함께 끄므로, 면제가 걸린 매니페스트에 선언을 추가하면 검증이 회복되지 않는다. 그 아래에서 대응 테스트가 없는 effect 이름 2건이 살아남아 있었고, 함께 정리했다.
647 lines
24 KiB
PHP
647 lines
24 KiB
PHP
<?php
|
|
|
|
namespace Modules\Sirsoft\Board\Tests\Feature;
|
|
|
|
// ModuleTestCase를 수동으로 require (autoload 전에 로드 필요)
|
|
require_once __DIR__.'/../ModuleTestCase.php';
|
|
|
|
use App\Extension\Helpers\NotificationSyncHelper;
|
|
use App\Extension\ModuleManager;
|
|
use App\Listeners\NotificationHookListener;
|
|
use App\Models\Permission;
|
|
use App\Models\Role;
|
|
use App\Models\User;
|
|
use App\Notifications\GenericNotification;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Notification;
|
|
use Modules\Sirsoft\Board\Enums\ReportStatus;
|
|
use Modules\Sirsoft\Board\Models\Board;
|
|
use Modules\Sirsoft\Board\Models\Report;
|
|
use Modules\Sirsoft\Board\Services\BoardSettingsService;
|
|
use Modules\Sirsoft\Board\Tests\ModuleTestCase;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
|
|
/**
|
|
* 신고 알림 통합 테스트 (이슈 #35 Phase 1 + Phase 2 + Phase 3)
|
|
*
|
|
* Phase 1: 신고 처리(블라인드/삭제/복원) 시 피신고자 알림 발송 검증
|
|
* Phase 2: 신고 접수 시 관리자 알림 발송 검증 (postTitle, reasonType 내용 포함)
|
|
* Phase 3: per_case/per_report 발송 범위 검증 (재신고/재활성화 시나리오)
|
|
* 실제 API 호출을 통해 훅 → 리스너 → 알림 전체 흐름을 검증합니다.
|
|
*/
|
|
class ReportNotificationTest extends ModuleTestCase
|
|
{
|
|
private User $admin;
|
|
|
|
private User $author;
|
|
|
|
private Board $board;
|
|
|
|
/**
|
|
* 테스트 환경 설정
|
|
*/
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
Notification::fake();
|
|
|
|
config(['telescope.enabled' => false]);
|
|
|
|
// module.php::getNotificationDefinitions() 를 SSoT 로 알림 정의 동기화 +
|
|
// NotificationHookListener 동적 훅 등록 (테스트 환경에서는 부트 시점에 실행되지 않음 —
|
|
// 알림 정의 기반 훅 구독을 수동으로 활성화하여 actual notification flow 복원)
|
|
$this->syncBoardNotificationDefinitions();
|
|
app(NotificationHookListener::class)->registerDynamicHooks();
|
|
|
|
// 테스트 잔여 데이터 정리
|
|
DB::table('boards_reports')->delete();
|
|
DB::table('users')->where('is_super', false)->delete();
|
|
|
|
// 사용자 생성
|
|
$this->admin = $this->createAdminUser([
|
|
'sirsoft-board.reports.view',
|
|
'sirsoft-board.reports.manage',
|
|
]);
|
|
$this->author = User::factory()->create();
|
|
|
|
// 게시판 생성
|
|
$this->board = Board::updateOrCreate(
|
|
['slug' => 'notify-test-board'],
|
|
[
|
|
'name' => ['ko' => '알림 테스트 게시판', 'en' => 'Notification Test Board'],
|
|
'slug' => 'notify-test-board',
|
|
'type' => 'list',
|
|
'per_page' => 20,
|
|
'per_page_mobile' => 10,
|
|
'order_by' => 'created_at',
|
|
'order_direction' => 'DESC',
|
|
'secret_mode' => 'disabled',
|
|
'use_comment' => true,
|
|
'use_reply' => false,
|
|
'use_file_upload' => false,
|
|
'use_report' => true,
|
|
'blocked_keywords' => [],
|
|
'notify_admin_on_post' => false,
|
|
'notify_author_on_comment' => false,
|
|
]
|
|
);
|
|
|
|
DB::table('boards_reports')->delete();
|
|
DB::table('board_comments')->where('board_id', $this->board->id)->delete();
|
|
DB::table('board_posts')->where('board_id', $this->board->id)->delete();
|
|
}
|
|
|
|
// ── report_policy.notify_author_on_report_action = ON ──
|
|
|
|
/**
|
|
* 신고 처리 정책 ON + suspended 상태 → 게시글 작성자에게 ReportActionNotification 발송
|
|
*/
|
|
#[Test]
|
|
public function test_게시글_신고_suspended_처리시_알림_발송(): void
|
|
{
|
|
// Given: 신고 처리 알림 정책 ON
|
|
$this->setReportPolicy(true);
|
|
|
|
$postId = $this->createTestPost($this->author->id);
|
|
|
|
$report = Report::factory()->create([
|
|
'board_id' => $this->board->id,
|
|
'target_type' => 'post',
|
|
'target_id' => $postId,
|
|
'status' => ReportStatus::Pending,
|
|
]);
|
|
|
|
// When: suspended 상태로 변경 (신고 처리 → blind)
|
|
$response = $this->actingAs($this->admin)
|
|
->patchJson("/api/modules/sirsoft-board/admin/reports/{$report->id}/status", [
|
|
'status' => 'suspended',
|
|
'process_note' => '신고 처리합니다.',
|
|
]);
|
|
|
|
// Then: 성공 응답
|
|
$response->assertStatus(200);
|
|
|
|
// 게시글 작성자에게 ReportActionNotification 발송 확인
|
|
Notification::assertSentTo($this->author, GenericNotification::class);
|
|
}
|
|
|
|
/**
|
|
* 신고 처리 정책 ON + deleted 상태 → 게시글 작성자에게 ReportActionNotification 발송
|
|
*/
|
|
#[Test]
|
|
public function test_게시글_신고_deleted_처리시_알림_발송(): void
|
|
{
|
|
// Given: 신고 처리 알림 정책 ON
|
|
$this->setReportPolicy(true);
|
|
|
|
$postId = $this->createTestPost($this->author->id);
|
|
|
|
$report = Report::factory()->create([
|
|
'board_id' => $this->board->id,
|
|
'target_type' => 'post',
|
|
'target_id' => $postId,
|
|
'status' => ReportStatus::Pending,
|
|
]);
|
|
|
|
// When: deleted 상태로 변경
|
|
$response = $this->actingAs($this->admin)
|
|
->patchJson("/api/modules/sirsoft-board/admin/reports/{$report->id}/status", [
|
|
'status' => 'deleted',
|
|
'process_note' => '삭제 처리합니다.',
|
|
]);
|
|
|
|
// Then: 성공 응답
|
|
$response->assertStatus(200);
|
|
|
|
// 게시글 작성자에게 ReportActionNotification 발송 확인
|
|
Notification::assertSentTo($this->author, GenericNotification::class);
|
|
}
|
|
|
|
/**
|
|
* 신고 처리 정책 ON + rejected(복원) 상태 → 게시글 작성자에게 ReportActionNotification 발송
|
|
*/
|
|
#[Test]
|
|
public function test_게시글_신고_rejected_복원시_알림_발송(): void
|
|
{
|
|
// Given: 신고 처리 알림 정책 ON + 블라인드된 게시글 + suspended 신고
|
|
$this->setReportPolicy(true);
|
|
|
|
$postId = $this->createTestPost($this->author->id, status: 'blinded', triggerType: 'report');
|
|
|
|
$report = Report::factory()->create([
|
|
'board_id' => $this->board->id,
|
|
'target_type' => 'post',
|
|
'target_id' => $postId,
|
|
'status' => ReportStatus::Suspended,
|
|
]);
|
|
|
|
// When: rejected 상태로 변경 (복원)
|
|
$response = $this->actingAs($this->admin)
|
|
->patchJson("/api/modules/sirsoft-board/admin/reports/{$report->id}/status", [
|
|
'status' => 'rejected',
|
|
'process_note' => '반려 처리합니다.',
|
|
]);
|
|
|
|
// Then: 성공 응답
|
|
$response->assertStatus(200);
|
|
|
|
// 게시글 작성자에게 ReportActionNotification 발송 확인
|
|
Notification::assertSentTo($this->author, GenericNotification::class);
|
|
}
|
|
|
|
// ── report_policy.notify_author_on_report_action = OFF ──
|
|
|
|
/**
|
|
* 신고 처리 정책 OFF → 알림 미발송
|
|
*/
|
|
#[Test]
|
|
public function test_게시글_신고_처리시_정책_off_알림_미발송(): void
|
|
{
|
|
// Given: 신고 처리 알림 정책 OFF
|
|
$this->setReportPolicy(false);
|
|
|
|
$postId = $this->createTestPost($this->author->id);
|
|
|
|
$report = Report::factory()->create([
|
|
'board_id' => $this->board->id,
|
|
'target_type' => 'post',
|
|
'target_id' => $postId,
|
|
'status' => ReportStatus::Pending,
|
|
]);
|
|
|
|
// When: suspended 상태로 변경
|
|
$this->actingAs($this->admin)
|
|
->patchJson("/api/modules/sirsoft-board/admin/reports/{$report->id}/status", [
|
|
'status' => 'suspended',
|
|
]);
|
|
|
|
// Then: 알림 미발송
|
|
Notification::assertNotSentTo($this->author, GenericNotification::class);
|
|
}
|
|
|
|
// ── 댓글 신고 처리 ──
|
|
|
|
/**
|
|
* 댓글 신고 처리 정책 ON + suspended → 댓글 작성자에게 ReportActionNotification 발송
|
|
*/
|
|
#[Test]
|
|
public function test_댓글_신고_suspended_처리시_알림_발송(): void
|
|
{
|
|
// Given: 신고 처리 알림 정책 ON
|
|
$this->setReportPolicy(true);
|
|
|
|
$postId = $this->createTestPost(User::factory()->create()->id);
|
|
$commentId = $this->createTestComment($postId, $this->author->id);
|
|
|
|
$report = Report::factory()->create([
|
|
'board_id' => $this->board->id,
|
|
'target_type' => 'comment',
|
|
'target_id' => $commentId,
|
|
'status' => ReportStatus::Pending,
|
|
]);
|
|
|
|
// When: suspended 상태로 변경
|
|
$response = $this->actingAs($this->admin)
|
|
->patchJson("/api/modules/sirsoft-board/admin/reports/{$report->id}/status", [
|
|
'status' => 'suspended',
|
|
'process_note' => '댓글 블라인드 처리합니다.',
|
|
]);
|
|
|
|
// Then: 성공 응답
|
|
$response->assertStatus(200);
|
|
|
|
// 댓글 작성자에게 ReportActionNotification 발송 확인
|
|
Notification::assertSentTo($this->author, GenericNotification::class);
|
|
}
|
|
|
|
/**
|
|
* 댓글 신고 처리 정책 OFF → 댓글 작성자에게 알림 미발송
|
|
*/
|
|
#[Test]
|
|
public function test_댓글_신고_처리시_정책_off_알림_미발송(): void
|
|
{
|
|
// Given: 신고 처리 알림 정책 OFF
|
|
$this->setReportPolicy(false);
|
|
|
|
$postId = $this->createTestPost(User::factory()->create()->id);
|
|
$commentId = $this->createTestComment($postId, $this->author->id);
|
|
|
|
$report = Report::factory()->create([
|
|
'board_id' => $this->board->id,
|
|
'target_type' => 'comment',
|
|
'target_id' => $commentId,
|
|
'status' => ReportStatus::Pending,
|
|
]);
|
|
|
|
// When: suspended 상태로 변경
|
|
$this->actingAs($this->admin)
|
|
->patchJson("/api/modules/sirsoft-board/admin/reports/{$report->id}/status", [
|
|
'status' => 'suspended',
|
|
]);
|
|
|
|
// Then: 알림 미발송
|
|
Notification::assertNotSentTo($this->author, GenericNotification::class);
|
|
}
|
|
|
|
// ── report_policy.notify_admin_on_report ──
|
|
|
|
/**
|
|
* 신고 접수 관리자 알림 정책 ON + reports.manage 권한 보유자 존재
|
|
* → 관리자에게 ReportReceivedAdminNotification 발송 (postTitle, reasonType 내용 포함)
|
|
*/
|
|
#[Test]
|
|
public function test_신고_접수시_정책_on_권한자에게_관리자알림_발송(): void
|
|
{
|
|
// Given: 관리자 알림 정책 ON
|
|
$this->setAdminReportPolicy(true);
|
|
|
|
$postId = $this->createTestPost(User::factory()->create()->id);
|
|
|
|
// When: 신고 접수
|
|
$reporter = User::factory()->create();
|
|
$response = $this->actingAs($reporter)
|
|
->postJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$postId}/reports", [
|
|
'reason_type' => 'spam',
|
|
'reason_detail' => '스팸 게시글입니다.',
|
|
]);
|
|
|
|
// Then: 성공 응답
|
|
$response->assertStatus(201);
|
|
|
|
// reports.manage 권한 보유자(admin)에게 알림 발송 확인
|
|
// post_title과 reason_type 내용도 검증 (reason_type 은 번역된 라벨 'Spam/Advertising' — 대소문자 무관 매칭)
|
|
Notification::assertSentTo(
|
|
$this->admin,
|
|
GenericNotification::class,
|
|
function (GenericNotification $notification) {
|
|
$data = $notification->getData();
|
|
|
|
return ($data['post_title'] ?? '') === '테스트 게시글'
|
|
&& stripos($data['reason_type'] ?? '', 'spam') !== false;
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 신고 접수 관리자 알림 정책 OFF → 관리자 알림 미발송
|
|
*/
|
|
#[Test]
|
|
public function test_신고_접수시_정책_off_관리자알림_미발송(): void
|
|
{
|
|
// Given: 관리자 알림 정책 OFF
|
|
$this->setAdminReportPolicy(false);
|
|
|
|
$postId = $this->createTestPost(User::factory()->create()->id);
|
|
|
|
// When: 신고 접수
|
|
$reporter = User::factory()->create();
|
|
$this->actingAs($reporter)
|
|
->postJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$postId}/reports", [
|
|
'reason_type' => 'spam',
|
|
'reason_detail' => '스팸 게시글입니다.',
|
|
]);
|
|
|
|
// Then: 관리자 알림 미발송
|
|
Notification::assertNotSentTo($this->admin, GenericNotification::class);
|
|
}
|
|
|
|
/**
|
|
* 신고 접수 관리자 알림 정책 ON + reports.manage 권한자 없음 → 알림 미발송
|
|
*/
|
|
#[Test]
|
|
public function test_신고_접수시_정책_on_권한자없음_알림_미발송(): void
|
|
{
|
|
// Given: 관리자 알림 정책 ON + admin 역할에서 권한 제거 (권한자 0명 상태 시뮬레이션)
|
|
$this->setAdminReportPolicy(true);
|
|
|
|
// admin 역할에서 reports.manage 권한 제거
|
|
$permission = Permission::where('identifier', 'sirsoft-board.reports.manage')->first();
|
|
$adminRole = Role::where('identifier', 'admin')->first();
|
|
if ($permission && $adminRole) {
|
|
$adminRole->permissions()->detach($permission->id);
|
|
}
|
|
|
|
$postId = $this->createTestPost(User::factory()->create()->id);
|
|
|
|
// When: 신고 접수
|
|
$reporter = User::factory()->create();
|
|
$response = $this->actingAs($reporter)
|
|
->postJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$postId}/reports", [
|
|
'reason_type' => 'abuse',
|
|
'reason_detail' => '욕설 게시글입니다.',
|
|
]);
|
|
|
|
// Then: 성공 응답 + 알림 미발송 (super_admin 폴백 없음)
|
|
$response->assertStatus(201);
|
|
Notification::assertNothingSent();
|
|
}
|
|
|
|
// ── per_case / per_report 발송 범위 ──
|
|
|
|
/**
|
|
* per_case: 동일 케이스에 재신고 → 두 번째 신고부터 알림 미발송
|
|
*/
|
|
#[Test]
|
|
public function test_per_case_재신고시_두번째부터_알림_미발송(): void
|
|
{
|
|
// Given: per_case 범위로 관리자 알림 정책 ON
|
|
$this->setAdminReportPolicy(true, 'per_case');
|
|
|
|
$postId = $this->createTestPost(User::factory()->create()->id);
|
|
|
|
// 첫 번째 신고 접수 → Report 생성 (케이스 신규)
|
|
$firstReporter = User::factory()->create();
|
|
$response = $this->actingAs($firstReporter)
|
|
->postJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$postId}/reports", [
|
|
'reason_type' => 'spam',
|
|
'reason_detail' => '첫 번째 신고',
|
|
]);
|
|
$response->assertStatus(201);
|
|
|
|
// 첫 신고 시 알림 발송 확인
|
|
Notification::assertSentTo($this->admin, GenericNotification::class);
|
|
|
|
// 알림 초기화
|
|
Notification::fake();
|
|
|
|
// 두 번째 신고 접수 (동일 케이스 재신고)
|
|
$secondReporter = User::factory()->create();
|
|
$response = $this->actingAs($secondReporter)
|
|
->postJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$postId}/reports", [
|
|
'reason_type' => 'abuse',
|
|
'reason_detail' => '두 번째 신고',
|
|
]);
|
|
$response->assertStatus(201);
|
|
|
|
// Then: 재신고 시 알림 미발송
|
|
Notification::assertNothingSent();
|
|
}
|
|
|
|
/**
|
|
* per_report: 동일 케이스에 재신고 → 매 신고마다 알림 발송
|
|
*/
|
|
#[Test]
|
|
public function test_per_report_재신고시_매번_알림_발송(): void
|
|
{
|
|
// Given: per_report 범위로 관리자 알림 정책 ON
|
|
$this->setAdminReportPolicy(true, 'per_report');
|
|
|
|
$postId = $this->createTestPost(User::factory()->create()->id);
|
|
|
|
// 첫 번째 신고
|
|
$firstReporter = User::factory()->create();
|
|
$response = $this->actingAs($firstReporter)
|
|
->postJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$postId}/reports", [
|
|
'reason_type' => 'spam',
|
|
'reason_detail' => '첫 번째 신고',
|
|
]);
|
|
$response->assertStatus(201);
|
|
|
|
Notification::assertSentTo($this->admin, GenericNotification::class);
|
|
|
|
// 알림 초기화
|
|
Notification::fake();
|
|
|
|
// 두 번째 신고 (동일 케이스 재신고)
|
|
$secondReporter = User::factory()->create();
|
|
$response = $this->actingAs($secondReporter)
|
|
->postJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$postId}/reports", [
|
|
'reason_type' => 'abuse',
|
|
'reason_detail' => '두 번째 신고',
|
|
]);
|
|
$response->assertStatus(201);
|
|
|
|
// Then: per_report 는 재신고도 알림 발송
|
|
Notification::assertSentTo($this->admin, GenericNotification::class);
|
|
}
|
|
|
|
/**
|
|
* per_case: 케이스 재활성화 후 첫 신고 → 알림 발송 (새 사이클 시작)
|
|
*
|
|
* 재활성화(last_activated_at 갱신) 이후 첫 신고 로그는 활성 사이클 내 1번째이므로
|
|
* per_case 조건에서도 알림이 발송되어야 합니다.
|
|
*/
|
|
#[Test]
|
|
public function test_per_case_재활성화후_첫신고시_알림_발송(): void
|
|
{
|
|
// Given: per_case 범위 + 이미 신고 로그가 있는 케이스 (재활성화 상태)
|
|
$this->setAdminReportPolicy(true, 'per_case');
|
|
|
|
$postId = $this->createTestPost(User::factory()->create()->id);
|
|
|
|
// 첫 신고 → 케이스 생성
|
|
$firstReporter = User::factory()->create();
|
|
$this->actingAs($firstReporter)
|
|
->postJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$postId}/reports", [
|
|
'reason_type' => 'spam',
|
|
'reason_detail' => '첫 신고',
|
|
]);
|
|
|
|
// 두 번째 신고 (같은 케이스, logs count = 2)
|
|
$this->resetListenerProcessedKeys();
|
|
$secondReporter = User::factory()->create();
|
|
$this->actingAs($secondReporter)
|
|
->postJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$postId}/reports", [
|
|
'reason_type' => 'abuse',
|
|
'reason_detail' => '두 번째 신고',
|
|
]);
|
|
|
|
// 케이스 재활성화: last_activated_at을 현재 시각으로 업데이트 (이전 로그들을 이전 사이클로 격리)
|
|
$report = Report::where('target_type', 'post')->where('target_id', $postId)->first();
|
|
$reactivatedAt = now()->addSecond(); // 기존 로그보다 미래로 설정
|
|
$report->update(['last_activated_at' => $reactivatedAt]);
|
|
|
|
// 알림 초기화 (재활성화 이전 알림 무시)
|
|
Notification::fake();
|
|
|
|
// 재활성화 이후 첫 신고
|
|
$thirdReporter = User::factory()->create();
|
|
$response = $this->actingAs($thirdReporter)
|
|
->postJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$postId}/reports", [
|
|
'reason_type' => 'spam',
|
|
'reason_detail' => '재활성화 후 첫 신고',
|
|
]);
|
|
$response->assertStatus(201);
|
|
|
|
// Then: 재활성화 후 첫 신고 → 새 사이클 시작이므로 알림 발송
|
|
Notification::assertSentTo($this->admin, GenericNotification::class);
|
|
}
|
|
|
|
// ── 헬퍼 메서드 ──
|
|
|
|
/**
|
|
* report_policy.notify_author_on_report_action 설정을 주입합니다.
|
|
*
|
|
* @param bool $enabled 알림 발송 여부
|
|
* @return void
|
|
*/
|
|
/**
|
|
* 알림 리스너의 중복 처리 키 초기화 (per_report/per_case 재발송 시나리오용).
|
|
*
|
|
* 현 설계에는 per-request 내부 dedup 상태가 존재하지 않으므로 no-op.
|
|
* 과거 어댑터에서 static 상태를 사용했을 때의 테스트 헬퍼 잔재로 호환성 보장용.
|
|
*/
|
|
private function resetListenerProcessedKeys(): void
|
|
{
|
|
// no-op: NotificationHookListener 는 상태 없이 매 훅 호출마다 정의를 재평가함
|
|
}
|
|
|
|
private function setReportPolicy(bool $enabled): void
|
|
{
|
|
config([
|
|
'g7_settings.modules.sirsoft-board.report_policy' => [
|
|
'notify_author_on_report_action' => $enabled,
|
|
'notify_author_on_report_action_channels' => ['mail'],
|
|
],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* report_policy.notify_admin_on_report 설정을 주입합니다.
|
|
*
|
|
* @param bool $enabled 알림 발송 여부
|
|
* @param string $scope 발송 범위 ('per_case' | 'per_report')
|
|
* @return void
|
|
*/
|
|
private function setAdminReportPolicy(bool $enabled, string $scope = 'per_case'): void
|
|
{
|
|
// checkAndApplyAutoHide()는 BoardSettingsService(파일)에서 읽으므로
|
|
// auto_hide_threshold를 충분히 높게 설정하여 테스트 중 자동 블라인드 방지
|
|
//
|
|
// 순서 주의: 설정 저장은 config 미러를 저장본 기준으로 다시 채운다. 그래서 아래
|
|
// config() 주입을 먼저 하면 이 호출이 그 주입을 덮어써 정책이 기본값(활성)으로 돌아간다.
|
|
/** @var BoardSettingsService $settingsService */
|
|
$settingsService = app(BoardSettingsService::class);
|
|
$settingsService->setSetting('report_policy.auto_hide_threshold', 100);
|
|
$settingsService->clearCache();
|
|
|
|
// g7_module_settings()는 Config에서 읽으므로 config 주입으로 알림 정책 제어
|
|
config([
|
|
'g7_settings.modules.sirsoft-board.report_policy' => [
|
|
'notify_admin_on_report' => $enabled,
|
|
'notify_admin_on_report_scope' => $scope,
|
|
'notify_admin_on_report_channels' => ['mail'],
|
|
],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 테스트용 게시글 생성 헬퍼
|
|
*
|
|
* @param int $userId 작성자 ID
|
|
* @param string $status 게시글 상태
|
|
* @param string $triggerType 처리 유형
|
|
* @return int 생성된 게시글 ID
|
|
*/
|
|
private function createTestPost(int $userId, string $status = 'published', string $triggerType = 'admin'): int
|
|
{
|
|
return DB::table('board_posts')->insertGetId([
|
|
'board_id' => $this->board->id,
|
|
'title' => '테스트 게시글',
|
|
'content' => '테스트 내용',
|
|
'user_id' => $userId,
|
|
'author_name' => '테스트 작성자',
|
|
'ip_address' => '127.0.0.1',
|
|
'is_notice' => false,
|
|
'is_secret' => false,
|
|
'status' => $status,
|
|
'trigger_type' => $triggerType,
|
|
'view_count' => 0,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 테스트용 댓글 생성 헬퍼
|
|
*
|
|
* @param int $postId 게시글 ID
|
|
* @param int $userId 작성자 ID
|
|
* @return int 생성된 댓글 ID
|
|
*/
|
|
private function createTestComment(int $postId, int $userId): int
|
|
{
|
|
return DB::table('board_comments')->insertGetId([
|
|
'board_id' => $this->board->id,
|
|
'post_id' => $postId,
|
|
'user_id' => $userId,
|
|
'author_name' => '테스트 댓글 작성자',
|
|
'content' => '테스트 댓글 내용',
|
|
'is_secret' => false,
|
|
'status' => 'published',
|
|
'trigger_type' => 'admin',
|
|
'depth' => 0,
|
|
'ip_address' => '127.0.0.1',
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* module.php::getNotificationDefinitions() 를 SSoT 로 게시판 알림 정의를 동기화합니다.
|
|
*
|
|
* declarative getter 패턴 도입(beta.4) 이후 별도 Seeder 가 없으므로 테스트 setUp
|
|
* 에서 직접 NotificationSyncHelper 를 호출해 알림 정의를 시드합니다.
|
|
*/
|
|
private function syncBoardNotificationDefinitions(): void
|
|
{
|
|
$module = app(ModuleManager::class)->getModule('sirsoft-board');
|
|
if (! $module) {
|
|
return;
|
|
}
|
|
|
|
$helper = app(NotificationSyncHelper::class);
|
|
foreach ($module->getNotificationDefinitions() as $data) {
|
|
$data['extension_type'] = 'module';
|
|
$data['extension_identifier'] = 'sirsoft-board';
|
|
|
|
$definition = $helper->syncDefinition($data);
|
|
|
|
foreach ($data['templates'] ?? [] as $template) {
|
|
$helper->syncTemplate($definition->id, $template);
|
|
}
|
|
}
|
|
}
|
|
}
|