shouldReceive('getRenderHint')->andReturn('text_code'); $manager = Mockery::mock(IdentityVerificationManager::class); // 첫 분기: manager->has(provider) === false → fallback 진입 유도 $manager->shouldReceive('has')->with('plugin:disabled')->andReturn(false); // 핵심 검증: fallback 분기에서 두 번째 인자로 정책의 provider_id 가 전달되어야 함 $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', ]); $hint = $this->invokeResolveRenderHint($service, $policy); $this->assertSame('text_code', $hint); } public function test_fallback_branch_passes_null_when_policy_has_no_provider_id(): void { $policyRepo = Mockery::mock(IdentityPolicyRepositoryInterface::class); $logRepo = Mockery::mock(IdentityVerificationLogRepositoryInterface::class); $resolvedProvider = Mockery::mock(IdentityVerificationInterface::class); $resolvedProvider->shouldReceive('getRenderHint')->andReturn('text_code'); $manager = Mockery::mock(IdentityVerificationManager::class); // provider_id 가 falsy 면 첫 if 조건 자체에서 fallback 진입 (manager->has 미호출) $manager->shouldReceive('resolveForPurpose') ->once() ->with('signup', null) ->andReturn($resolvedProvider); $service = new IdentityPolicyService($policyRepo, $logRepo, $manager); $policy = new IdentityPolicy([ 'purpose' => 'signup', 'provider_id' => null, ]); $hint = $this->invokeResolveRenderHint($service, $policy); $this->assertSame('text_code', $hint); } public function test_first_branch_returns_provider_hint_without_fallback(): void { $policyRepo = Mockery::mock(IdentityPolicyRepositoryInterface::class); $logRepo = Mockery::mock(IdentityVerificationLogRepositoryInterface::class); $directProvider = Mockery::mock(IdentityVerificationInterface::class); $directProvider->shouldReceive('getRenderHint')->andReturn('redirect_oauth'); $manager = Mockery::mock(IdentityVerificationManager::class); $manager->shouldReceive('has')->with('plugin:active')->andReturn(true); $manager->shouldReceive('get')->with('plugin:active')->andReturn($directProvider); // 첫 분기 통과 시 resolveForPurpose 는 호출되어선 안 됨 (회귀 차단) $manager->shouldNotReceive('resolveForPurpose'); $service = new IdentityPolicyService($policyRepo, $logRepo, $manager); $policy = new IdentityPolicy([ 'purpose' => 'signup', 'provider_id' => 'plugin:active', ]); $hint = $this->invokeResolveRenderHint($service, $policy); $this->assertSame('redirect_oauth', $hint); } /** * protected resolveRenderHint 를 reflection 으로 직접 호출합니다. */ private function invokeResolveRenderHint(IdentityPolicyService $service, IdentityPolicy $policy): ?string { $reflection = new \ReflectionMethod(IdentityPolicyService::class, 'resolveRenderHint'); $reflection->setAccessible(true); /** @var string|null $hint */ $hint = $reflection->invoke($service, $policy); return $hint; } }