공개 (@Tuwasduliebst 제보) 대응. - ckeditor5: 미참조 이미지 정리 엔진 신설 — 참조 판정(hash + 파일명 2토큰, filter_reference_sources 훅으로 확장 콘텐츠 등록) + 정리 커맨드(기본 꺼짐 옵트인, 3중 게이트) + 업로드 관리 화면(권한 분리·참조 배지·개별/선택 삭제) + 기설치본 설정 백필 upgrade step - board: 임시 첨부 GC(상시) + 소프트삭제 첨부 영구 정리(옵트인) + 설정 UI, 저장 payload 누락(F4)·빈 temp 디렉토리 잔존(F5) 수정 - ecommerce: 임시 상품 이미지 GC + 카테고리 삭제 시 이미지 파일 미삭제(S4) 수정 - page: 임시 첨부 GC - 코어: 고아 첨부 정리 커맨드(옵트인, site_logo 보호) + 사이트 로고 교체 시 구 로고 재편입 차단(S6) + DB 백업 미구현 501 정직화(B3) + 플러그인 관리자 메뉴 설치·업데이트 자동 동기화(F1·F7) - 동반: 플러그인 설정 검증 오류 필드명 라벨화(F3), Toggle 노드 type 정정 수평 전개(marketing 1.0.2 bump·gdpr·ecommerce), ja 언어팩 4종, API 문서, hooks.md, 시나리오 매니페스트 8종, Playwright E2E 4종
155 lines
5.2 KiB
PHP
155 lines
5.2 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Api\Admin;
|
|
|
|
use App\Enums\ExtensionOwnerType;
|
|
use App\Models\Permission;
|
|
use App\Models\Role;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* DB 백업 엔드포인트의 미제공 응답 검증 (공개 #115 부록 B3)
|
|
*
|
|
* `POST /api/admin/settings/backup-database` 는 존재하지 않는
|
|
* `SettingsService::backupDatabase()` 를 호출해 왔다. PHP 가 던지는 것은 `Error` 라
|
|
* 컨트롤러의 `catch (\Exception)` 에 걸리지 않고 그대로 500 이 됐다.
|
|
* 화면 버튼이 없어 운영 중에는 드러나지 않지만, 공개 API 레퍼런스에는 정상 기능으로
|
|
* 실려 있어 문서를 보고 호출한 쪽에는 무조건 서버 오류였다.
|
|
*
|
|
* 기능은 아직 없으므로 "서버가 고장났다"(500)가 아니라 "이 기능은 제공하지 않는다"(501)로
|
|
* 정직하게 답하는 것이 계약이다.
|
|
*/
|
|
class SettingsDatabaseBackupTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private User $admin;
|
|
|
|
private string $token;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->admin = $this->createAdminUser();
|
|
$this->token = $this->admin->createToken('test-token')->plainTextToken;
|
|
}
|
|
|
|
/**
|
|
* 설정 갱신 권한을 가진 관리자 생성
|
|
*/
|
|
private function createAdminUser(): User
|
|
{
|
|
$user = User::factory()->create([
|
|
'password' => Hash::make('password123'),
|
|
]);
|
|
|
|
$permissionIds = [];
|
|
foreach (['core.settings.read', 'core.settings.update'] as $permIdentifier) {
|
|
$permission = Permission::firstOrCreate(
|
|
['identifier' => $permIdentifier],
|
|
[
|
|
'name' => json_encode(['ko' => $permIdentifier, 'en' => $permIdentifier]),
|
|
'description' => json_encode(['ko' => $permIdentifier, 'en' => $permIdentifier]),
|
|
'extension_type' => ExtensionOwnerType::Core,
|
|
'extension_identifier' => 'core',
|
|
'type' => 'admin',
|
|
]
|
|
);
|
|
$permissionIds[] = $permission->id;
|
|
}
|
|
|
|
$roleIdentifier = 'admin_test_'.uniqid();
|
|
$adminRole = Role::create([
|
|
'identifier' => $roleIdentifier,
|
|
'name' => json_encode(['ko' => '테스트 관리자', 'en' => 'Test Admin']),
|
|
'description' => json_encode(['ko' => '테스트', 'en' => 'Test']),
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$adminBaseRole = Role::firstOrCreate(
|
|
['identifier' => 'admin'],
|
|
[
|
|
'name' => json_encode(['ko' => '관리자', 'en' => 'Administrator']),
|
|
'description' => json_encode(['ko' => '시스템 관리자', 'en' => 'System Administrator']),
|
|
'extension_type' => ExtensionOwnerType::Core,
|
|
'extension_identifier' => 'core',
|
|
'is_active' => true,
|
|
]
|
|
);
|
|
|
|
$adminRole->permissions()->sync($permissionIds);
|
|
|
|
$user->roles()->attach($adminBaseRole->id, [
|
|
'assigned_at' => now(),
|
|
'assigned_by' => null,
|
|
]);
|
|
$user->roles()->attach($adminRole->id, [
|
|
'assigned_at' => now(),
|
|
'assigned_by' => null,
|
|
]);
|
|
|
|
return $user->fresh();
|
|
}
|
|
|
|
/**
|
|
* 인증된 요청 헬퍼
|
|
*/
|
|
private function authRequest(): static
|
|
{
|
|
return $this->withHeaders([
|
|
'Authorization' => 'Bearer '.$this->token,
|
|
'Accept' => 'application/json',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 미제공 기능은 500 이 아니라 501 로 응답해야 한다.
|
|
*/
|
|
#[Test]
|
|
public function database_backup_responds_with_not_implemented(): void
|
|
{
|
|
$response = $this->authRequest()->postJson('/api/admin/settings/backup-database');
|
|
|
|
$response->assertStatus(501);
|
|
$response->assertJson(['success' => false]);
|
|
}
|
|
|
|
/**
|
|
* 응답 메시지는 다국어 키가 해석된 사람 말이어야 한다.
|
|
*
|
|
* 키 원문(`settings.database_backup_unavailable`)이 그대로 나가면 호출자는
|
|
* 무슨 상태인지 알 수 없다.
|
|
*/
|
|
#[Test]
|
|
public function not_implemented_response_carries_a_translated_message(): void
|
|
{
|
|
$response = $this->authRequest()->postJson('/api/admin/settings/backup-database');
|
|
|
|
$message = (string) $response->json('message');
|
|
|
|
$this->assertNotSame('', $message);
|
|
$this->assertStringNotContainsString('settings.', $message);
|
|
$this->assertSame(__('settings.database_backup_unavailable'), $message);
|
|
}
|
|
|
|
/**
|
|
* 미구현 응답으로 바꾸더라도 권한 경계는 그대로여야 한다.
|
|
*
|
|
* 501 을 먼저 돌려주느라 인증·권한 검사를 건너뛰면, 권한 없는 요청에까지
|
|
* 내부 구현 상태를 알려주게 된다.
|
|
*/
|
|
#[Test]
|
|
public function guest_still_receives_unauthenticated(): void
|
|
{
|
|
$response = $this->withHeaders(['Accept' => 'application/json'])
|
|
->postJson('/api/admin/settings/backup-database');
|
|
|
|
$response->assertStatus(401);
|
|
}
|
|
}
|