* @category Class * @license https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License * @link www.splendidbear.org * @since 2026. 04. 21. */ #[AllowMockObjectsWithoutExpectations] #[TestDox('Recaptcha Service')] class RecaptchaServiceTest extends TestCase { private const SECRET_KEY = 'test-secret-key'; #[Test] #[TestDox('Verify returns false for empty token')] public function verifyReturnsFalseForEmptyToken(): void { $httpClient = $this->createMock(HttpClientInterface::class); $logger = $this->createMock(LoggerInterface::class); $service = new RecaptchaService($httpClient, $logger, self::SECRET_KEY); $result = $service->verify(''); $this->assertFalse($result); } #[Test] #[TestDox('Verify returns false when api returns failure')] public function verifyReturnsFalseWhenApiReturnsFailure(): void { $mockResponse = new MockResponse(json_encode([ 'success' => false, 'error-codes' => ['invalid-input-secret'], ], JSON_THROW_ON_ERROR)); $httpClient = new MockHttpClient($mockResponse); $logger = $this->createMock(LoggerInterface::class); $logger->expects($this->once())->method('info'); $service = new RecaptchaService($httpClient, $logger, self::SECRET_KEY); $result = $service->verify('invalid-token'); $this->assertFalse($result); } #[Test] #[TestDox('Verify returns true when api returns success and high score')] public function verifyReturnsTrueWhenApiReturnsSuccessAndHighScore(): void { $mockResponse = new MockResponse(json_encode([ 'success' => true, 'score' => 0.8, 'hostname' => 'test.com', ], JSON_THROW_ON_ERROR)); $httpClient = new MockHttpClient($mockResponse); $logger = $this->createMock(LoggerInterface::class); $logger->expects($this->once())->method('info'); $service = new RecaptchaService($httpClient, $logger, self::SECRET_KEY); $result = $service->verify('valid-token'); $this->assertTrue($result); } #[Test] #[TestDox('Verify returns false when score below threshold')] public function verifyReturnsFalseWhenScoreBelowThreshold(): void { $mockResponse = new MockResponse(json_encode([ 'success' => true, 'score' => 0.3, 'hostname' => 'test.com', ], JSON_THROW_ON_ERROR)); $httpClient = new MockHttpClient($mockResponse); $logger = $this->createMock(LoggerInterface::class); $logger->expects($this->once())->method('info'); $service = new RecaptchaService($httpClient, $logger, self::SECRET_KEY); $result = $service->verify('low-score-token'); $this->assertFalse($result); } #[Test] #[TestDox('Verify returns false when api throws exception')] public function verifyReturnsFalseWhenApiThrowsException(): void { $mockResponse = new MockResponse('', ['http_code' => 500]); $httpClient = new MockHttpClient($mockResponse); $logger = $this->createMock(LoggerInterface::class); $logger->expects($this->once())->method('error'); $service = new RecaptchaService($httpClient, $logger, self::SECRET_KEY); $result = $service->verify('test-token'); $this->assertFalse($result); } #[Test] #[TestDox('Verify includes remote ip when provided')] public function verifyIncludesRemoteIpWhenProvided(): void { $mockResponse = new MockResponse(json_encode([ 'success' => true, 'score' => 0.9, ], JSON_THROW_ON_ERROR)); $httpClient = new MockHttpClient($mockResponse); $logger = $this->createMock(LoggerInterface::class); $logger->expects($this->once())->method('info'); $service = new RecaptchaService($httpClient, $logger, self::SECRET_KEY); $result = $service->verify('test-token', '192.168.1.1'); $this->assertTrue($result); } #[Test] #[TestDox('Verify with score at threshold')] public function verifyWithScoreAtThreshold(): void { $mockResponse = new MockResponse(json_encode([ 'success' => true, 'score' => 0.5, ], JSON_THROW_ON_ERROR)); $httpClient = new MockHttpClient($mockResponse); $logger = $this->createMock(LoggerInterface::class); $service = new RecaptchaService($httpClient, $logger, self::SECRET_KEY); $result = $service->verify('threshold-token'); $this->assertTrue($result); } #[Test] #[TestDox('Verify with no score fails')] public function verifyWithNoScoreFails(): void { $mockResponse = new MockResponse(json_encode([ 'success' => true, ], JSON_THROW_ON_ERROR)); $httpClient = new MockHttpClient($mockResponse); $logger = $this->createMock(LoggerInterface::class); $service = new RecaptchaService($httpClient, $logger, self::SECRET_KEY); $result = $service->verify('no-score-token'); $this->assertFalse($result); } }