seed(IdentityMessageDefinitionSeeder::class); $logRepository = $this->app->make(IdentityVerificationLogRepositoryInterface::class); $this->provider = new MailIdentityProvider($logRepository); } /** * purpose 별 challenge 발급 회로 — log 상태 'sent' + 적절한 render_hint + 메타데이터에 hash 저장. * * 메일 본문 평문 코드는 보안상 hash 만 저장되므로 본 테스트는 발송이 트리거되었음을 'sent' 상태로 검증. * GenericNotification 의 via() 채널 평가는 NotificationDefinition/readiness 체인 의존이라 * 테스트 환경에서 별도 시드 없이는 fake 캡처가 불가 — 회로 자체 검증으로 충분. * * @dataProvider purposesProvider */ public function test_mail_provider_circuit_for_purpose(string $purpose, string $expectedRenderHint, string $expectedHashKey): void { $user = User::factory()->create(); $challenge = $this->provider->requestChallenge($user, ['purpose' => $purpose]); $this->assertSame($expectedRenderHint, $challenge->renderHint, "purpose '{$purpose}' 의 render_hint 가 기대값과 일치해야 함"); $this->assertNotEmpty($challenge->id); $log = \DB::table('identity_verification_logs')->where('id', $challenge->id)->first(); $this->assertNotNull($log); $this->assertSame('sent', $log->status, "purpose '{$purpose}' challenge 발급 후 log status='sent' 여야 함"); $this->assertSame($purpose, $log->purpose); $this->assertSame($user->id, $log->user_id); $metadata = json_decode($log->metadata, true); $this->assertArrayHasKey( $expectedHashKey, $metadata, "purpose '{$purpose}' 의 metadata 에 {$expectedHashKey} 가 저장되어 있어야 함", ); } public static function purposesProvider(): array { return [ 'signup → text_code' => ['signup', 'text_code', 'code_hash'], 'sensitive_action → text_code' => ['sensitive_action', 'text_code', 'code_hash'], 'self_update → text_code' => ['self_update', 'text_code', 'code_hash'], 'password_reset → link' => ['password_reset', 'link', 'link_token_hash'], ]; } /** * 코드 발송 회로 — 발송된 challenge 가 DB 로그에 'sent' 상태로 기록되는지 추가 확인. * 메일 본문 평문 코드는 보안상 hash 만 저장하므로 실재만 검증. */ public function test_mail_provider_logs_challenge_with_sent_status(): void { $user = User::factory()->create(); $challenge = $this->provider->requestChallenge($user, ['purpose' => 'sensitive_action']); $log = \DB::table('identity_verification_logs')->where('id', $challenge->id)->first(); $this->assertNotNull($log); $this->assertSame('sent', $log->status); $this->assertSame($user->id, $log->user_id); $this->assertSame('sensitive_action', $log->purpose); $metadata = json_decode($log->metadata, true); $this->assertArrayHasKey('code_hash', $metadata, 'code_hash 가 metadata 에 저장되어 있어야 함 (평문은 발송 메일에만 노출)'); } }