Files
Gnuboard7/tests/Unit/Resources/BaseApiResourceMetaTest.php
2026-05-11 11:29:41 +09:00

300 lines
9.9 KiB
PHP

<?php
namespace Tests\Unit\Resources;
use App\Http\Resources\BaseApiResource;
use App\Models\Permission;
use App\Models\Role;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Request;
use Tests\TestCase;
/**
* BaseApiResource의 resourceMeta() 메서드 단위 테스트
*
* ownerField(), abilityMap(), resolveIsOwner(), resolveAbilities(),
* checkAbility(), resourceMeta()를 테스트합니다.
*/
class BaseApiResourceMetaTest extends TestCase
{
use RefreshDatabase;
private Role $adminRole;
private Role $userRole;
private User $adminUser;
private User $regularUser;
protected function setUp(): void
{
parent::setUp();
$this->seed();
// Role 생성
$this->adminRole = Role::where('identifier', 'admin')->first()
?? Role::create([
'identifier' => 'admin',
'name' => ['ko' => '관리자', 'en' => 'Administrator'],
]);
$this->userRole = Role::where('identifier', 'user')->first()
?? Role::create([
'identifier' => 'user',
'name' => ['ko' => '일반 사용자', 'en' => 'Regular User'],
]);
// 테스트용 Permission 생성
$permission = Permission::create([
'identifier' => 'test-module.products.update',
'name' => ['ko' => '상품 수정', 'en' => 'Update Products'],
'type' => 'admin',
]);
$this->adminRole->permissions()->syncWithoutDetaching([$permission->id]);
$permission2 = Permission::create([
'identifier' => 'test-module.products.delete',
'name' => ['ko' => '상품 삭제', 'en' => 'Delete Products'],
'type' => 'admin',
]);
$this->adminRole->permissions()->syncWithoutDetaching([$permission2->id]);
// User 생성
$this->adminUser = User::factory()->create(['email' => 'admin-meta@example.com']);
$this->adminUser->roles()->syncWithoutDetaching([$this->adminRole->id]);
$this->regularUser = User::factory()->create(['email' => 'user-meta@example.com']);
$this->regularUser->roles()->syncWithoutDetaching([$this->userRole->id]);
}
// =========================================================================
// ownerField / resolveIsOwner 테스트
// =========================================================================
/**
* ownerField가 null이면 is_owner가 resourceMeta에 포함되지 않는다.
*/
public function test_resource_meta_excludes_is_owner_when_owner_field_is_null(): void
{
$resource = new class(['id' => 1]) extends BaseApiResource {
// ownerField() 기본값 = null
};
$request = Request::create('/test');
$meta = $this->invokeResourceMeta($resource, $request);
$this->assertArrayNotHasKey('is_owner', $meta);
}
/**
* ownerField가 정의되고 요청 사용자가 소유자이면 is_owner = true.
*
* 실제 사용처는 Eloquent Model 래핑 (`$this->resource->{$field}` 은 객체 프로퍼티 접근)
* 이므로 테스트도 stdClass / Model 인스턴스를 사용해야 한다 (배열로 주입 시 에러).
*/
public function test_resource_meta_returns_is_owner_true_when_user_is_owner(): void
{
$data = (object) ['id' => 1, 'user_id' => $this->adminUser->id];
$resource = new class($data) extends BaseApiResource {
protected function ownerField(): ?string
{
return 'user_id';
}
};
$request = Request::create('/test');
$request->setUserResolver(fn () => $this->adminUser);
$meta = $this->invokeResourceMeta($resource, $request);
$this->assertArrayHasKey('is_owner', $meta);
$this->assertTrue($meta['is_owner']);
}
/**
* ownerField가 정의되고 요청 사용자가 소유자가 아니면 is_owner = false.
*/
public function test_resource_meta_returns_is_owner_false_when_user_is_not_owner(): void
{
$data = (object) ['id' => 1, 'user_id' => 999];
$resource = new class($data) extends BaseApiResource {
protected function ownerField(): ?string
{
return 'user_id';
}
};
$request = Request::create('/test');
$request->setUserResolver(fn () => $this->regularUser);
$meta = $this->invokeResourceMeta($resource, $request);
$this->assertArrayHasKey('is_owner', $meta);
$this->assertFalse($meta['is_owner']);
}
/**
* ownerField가 정의되고 비로그인 사용자이면 is_owner = false.
*/
public function test_resource_meta_returns_is_owner_false_for_guest(): void
{
$data = (object) ['id' => 1, 'user_id' => 1];
$resource = new class($data) extends BaseApiResource {
protected function ownerField(): ?string
{
return 'user_id';
}
};
$request = Request::create('/test');
$request->setUserResolver(fn () => null);
$meta = $this->invokeResourceMeta($resource, $request);
$this->assertArrayHasKey('is_owner', $meta);
$this->assertFalse($meta['is_owner']);
}
// =========================================================================
// abilityMap / resolveAbilities 테스트
// =========================================================================
/**
* abilityMap이 비어있으면 abilities가 resourceMeta에 포함되지 않는다.
*/
public function test_resource_meta_excludes_abilities_when_permission_map_is_empty(): void
{
$resource = new class(['id' => 1]) extends BaseApiResource {
// abilityMap() 기본값 = []
};
$request = Request::create('/test');
$meta = $this->invokeResourceMeta($resource, $request);
$this->assertArrayNotHasKey('abilities', $meta);
}
/**
* Admin 역할 사용자는 모든 권한이 true.
*/
public function test_resource_meta_returns_all_true_for_admin_user(): void
{
$resource = $this->createResourceWithPermissionMap();
$request = Request::create('/test');
$request->setUserResolver(fn () => $this->adminUser);
$meta = $this->invokeResourceMeta($resource, $request);
$this->assertArrayHasKey('abilities', $meta);
$this->assertTrue($meta['abilities']['can_update']);
$this->assertTrue($meta['abilities']['can_delete']);
}
/**
* 일반 사용자는 권한이 없으면 false.
*/
public function test_resource_meta_returns_false_for_user_without_abilities(): void
{
$resource = $this->createResourceWithPermissionMap();
$request = Request::create('/test');
$request->setUserResolver(fn () => $this->regularUser);
$meta = $this->invokeResourceMeta($resource, $request);
$this->assertArrayHasKey('abilities', $meta);
$this->assertFalse($meta['abilities']['can_update']);
$this->assertFalse($meta['abilities']['can_delete']);
}
// =========================================================================
// resourceMeta 통합 테스트
// =========================================================================
/**
* ownerField + abilityMap이 모두 정의되면 is_owner + abilities 모두 반환.
*/
public function test_resource_meta_returns_both_is_owner_and_abilities(): void
{
$data = (object) ['id' => 1, 'user_id' => $this->adminUser->id];
$resource = new class($data) extends BaseApiResource {
protected function ownerField(): ?string
{
return 'user_id';
}
protected function abilityMap(): array
{
return [
'can_update' => 'test-module.products.update',
'can_delete' => 'test-module.products.delete',
];
}
};
$request = Request::create('/test');
$request->setUserResolver(fn () => $this->adminUser);
$meta = $this->invokeResourceMeta($resource, $request);
$this->assertArrayHasKey('is_owner', $meta);
$this->assertTrue($meta['is_owner']);
$this->assertArrayHasKey('abilities', $meta);
$this->assertCount(2, $meta['abilities']);
}
/**
* ownerField, abilityMap 모두 미정의면 빈 배열 반환.
*/
public function test_resource_meta_returns_empty_array_when_nothing_defined(): void
{
$resource = new class(['id' => 1]) extends BaseApiResource {};
$request = Request::create('/test');
$meta = $this->invokeResourceMeta($resource, $request);
$this->assertEmpty($meta);
}
// =========================================================================
// 헬퍼 메서드
// =========================================================================
/**
* abilityMap이 정의된 테스트용 리소스를 생성합니다.
*/
private function createResourceWithPermissionMap(): BaseApiResource
{
return new class(['id' => 1]) extends BaseApiResource {
protected function abilityMap(): array
{
return [
'can_update' => 'test-module.products.update',
'can_delete' => 'test-module.products.delete',
];
}
};
}
/**
* resourceMeta를 호출합니다.
*
* @param BaseApiResource $resource 리소스 인스턴스
* @param Request $request HTTP 요청 객체
* @return array 메타 데이터
*/
private function invokeResourceMeta(BaseApiResource $resource, Request $request): array
{
$reflection = new \ReflectionMethod($resource, 'resourceMeta');
$reflection->setAccessible(true);
return $reflection->invoke($resource, $request);
}
}