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); } }