Files
Gnuboard7/tests/Unit/Services/IdentityPolicyServiceResolveProviderIdTest.php
T
2026-07-01 10:30:32 +09:00

137 lines
5.6 KiB
PHP

<?php
namespace Tests\Unit\Services;
use App\Contracts\Extension\IdentityVerificationInterface;
use App\Contracts\Repositories\IdentityPolicyRepositoryInterface;
use App\Contracts\Repositories\IdentityVerificationLogRepositoryInterface;
use App\Extension\IdentityVerification\IdentityVerificationManager;
use App\Models\IdentityPolicy;
use App\Services\IdentityPolicyService;
use Mockery;
use Tests\TestCase;
/**
* IdentityPolicyService::resolveProviderId 의 fallback 분기 회귀 차단.
*
* 이슈 #275 — 정책 DB 의 provider_id 가 NULL 인 경우 enforce() 가 던지는 428 응답의
* verification.provider_id 가 그대로 NULL 로 흘러가 launcher 가 외부 plugin Extension Point
* 슬롯과 매칭 못해 빈 모달이 표시되던 결함을 차단한다.
*
* 본 테스트는 두 가지 분기를 검증한다:
* (1) 정책의 provider_id 가 등록된 provider id 면 그대로 반환
* (2) 정책의 provider_id 가 NULL 또는 미등록이면 Manager 의 purpose 기반 fallback 체인을 따름
*
* resolveProviderId 는 Service::enforce() 가 throw 할 때 IdentityVerificationRequiredException
* 의 providerId 인자로 전달되는 값을 결정하므로, 본 회귀 차단이 통과해야 admin UI 의
* default_provider 환경설정이 모달 launcher 까지 정상 전달된다.
*/
class IdentityPolicyServiceResolveProviderIdTest extends TestCase
{
protected function tearDown(): void
{
Mockery::close();
parent::tearDown();
}
public function test_returns_policy_provider_id_when_registered(): void
{
$policyRepo = Mockery::mock(IdentityPolicyRepositoryInterface::class);
$logRepo = Mockery::mock(IdentityVerificationLogRepositoryInterface::class);
$manager = Mockery::mock(IdentityVerificationManager::class);
// 등록된 provider 면 그대로 반환 — Manager fallback 진입 없음
$manager->shouldReceive('has')->with('inicis')->andReturn(true);
$manager->shouldNotReceive('resolveForPurpose');
$service = new IdentityPolicyService($policyRepo, $logRepo, $manager);
$policy = new IdentityPolicy([
'purpose' => 'signup',
'provider_id' => 'inicis',
]);
$this->assertSame('inicis', $this->invokeResolveProviderId($service, $policy));
}
public function test_falls_back_to_manager_when_policy_provider_id_is_null(): void
{
$policyRepo = Mockery::mock(IdentityPolicyRepositoryInterface::class);
$logRepo = Mockery::mock(IdentityVerificationLogRepositoryInterface::class);
$resolvedProvider = Mockery::mock(IdentityVerificationInterface::class);
$resolvedProvider->shouldReceive('getId')->andReturn('inicis');
$manager = Mockery::mock(IdentityVerificationManager::class);
// 정책 provider_id 가 NULL → 즉시 fallback 분기
$manager->shouldReceive('resolveForPurpose')
->once()
->with('signup', null)
->andReturn($resolvedProvider);
$service = new IdentityPolicyService($policyRepo, $logRepo, $manager);
$policy = new IdentityPolicy([
'purpose' => 'signup',
'provider_id' => null,
]);
$this->assertSame('inicis', $this->invokeResolveProviderId($service, $policy));
}
public function test_falls_back_to_manager_when_policy_provider_unregistered(): void
{
$policyRepo = Mockery::mock(IdentityPolicyRepositoryInterface::class);
$logRepo = Mockery::mock(IdentityVerificationLogRepositoryInterface::class);
$resolvedProvider = Mockery::mock(IdentityVerificationInterface::class);
$resolvedProvider->shouldReceive('getId')->andReturn('g7:core.mail');
$manager = Mockery::mock(IdentityVerificationManager::class);
// 정책 provider 가 비활성/언인스톨된 경우 — has()=false → fallback 진입
$manager->shouldReceive('has')->with('plugin:disabled')->andReturn(false);
// 핵심 회귀 차단: 정책의 provider_id 를 Manager 의 0번 우선순위로 전달
$manager->shouldReceive('resolveForPurpose')
->once()
->with('signup', 'plugin:disabled')
->andReturn($resolvedProvider);
$service = new IdentityPolicyService($policyRepo, $logRepo, $manager);
$policy = new IdentityPolicy([
'purpose' => 'signup',
'provider_id' => 'plugin:disabled',
]);
$this->assertSame('g7:core.mail', $this->invokeResolveProviderId($service, $policy));
}
public function test_returns_original_provider_id_when_manager_throws(): void
{
$policyRepo = Mockery::mock(IdentityPolicyRepositoryInterface::class);
$logRepo = Mockery::mock(IdentityVerificationLogRepositoryInterface::class);
$manager = Mockery::mock(IdentityVerificationManager::class);
$manager->shouldReceive('resolveForPurpose')
->andThrow(new \InvalidArgumentException('No provider supports purpose'));
$service = new IdentityPolicyService($policyRepo, $logRepo, $manager);
$policy = new IdentityPolicy([
'purpose' => 'unknown',
'provider_id' => 'legacy-id',
]);
// Manager 예외 발생 시 정책의 원본 provider_id 를 그대로 반환 (정책 정보 보존)
$this->assertSame('legacy-id', $this->invokeResolveProviderId($service, $policy));
}
private function invokeResolveProviderId(IdentityPolicyService $service, IdentityPolicy $policy): ?string
{
$reflection = new \ReflectionMethod($service, 'resolveProviderId');
$reflection->setAccessible(true);
return $reflection->invoke($service, $policy);
}
}