diff --git a/src/Providers/Http/Exception/ServerException.php b/src/Providers/Http/Exception/ServerException.php index b366c719..bc4b63de 100644 --- a/src/Providers/Http/Exception/ServerException.php +++ b/src/Providers/Http/Exception/ServerException.php @@ -38,6 +38,7 @@ public static function fromServerErrorResponse(Response $response): self 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 507 => 'Insufficient Storage', + 529 => 'Overloaded', ]; if (isset($statusTexts[$statusCode])) { diff --git a/tests/unit/Exceptions/ExceptionsTest.php b/tests/unit/Exceptions/ExceptionsTest.php index fe3b88b0..8dd5315c 100644 --- a/tests/unit/Exceptions/ExceptionsTest.php +++ b/tests/unit/Exceptions/ExceptionsTest.php @@ -9,8 +9,10 @@ use WordPress\AiClient\Common\Exception\InvalidArgumentException; use WordPress\AiClient\Common\Exception\RuntimeException; use WordPress\AiClient\Common\Exception\TokenLimitReachedException; +use WordPress\AiClient\Providers\Http\DTO\Response; use WordPress\AiClient\Providers\Http\Exception\ClientException; use WordPress\AiClient\Providers\Http\Exception\NetworkException; +use WordPress\AiClient\Providers\Http\Exception\ServerException; /** * Tests for AI Client exceptions. @@ -21,6 +23,7 @@ * @covers \WordPress\AiClient\Common\Exception\TokenLimitReachedException * @covers \WordPress\AiClient\Providers\Http\Exception\NetworkException * @covers \WordPress\AiClient\Providers\Http\Exception\ClientException + * @covers \WordPress\AiClient\Providers\Http\Exception\ServerException */ class ExceptionsTest extends TestCase { @@ -32,6 +35,7 @@ public function testAllExceptionsImplementAiClientExceptionInterface(): void new TokenLimitReachedException('test'), new NetworkException('test'), new ClientException('test'), + new ServerException('test'), ]; foreach ($exceptions as $exception) { @@ -68,6 +72,7 @@ public function testCatchAllFunctionality(): void new TokenLimitReachedException('token limit error'), new NetworkException('network error'), new ClientException('client error'), + new ServerException('server error'), ]; foreach ($exceptions as $exception) { @@ -81,4 +86,102 @@ public function testCatchAllFunctionality(): void $this->assertTrue($caught, 'Exception should be catchable as AiClientExceptionInterface'); } } + + public function testServerExceptionExtendsRuntimeException(): void + { + $exception = new ServerException('server error'); + + $this->assertInstanceOf(RuntimeException::class, $exception); + } + + /** + * @dataProvider knownServerStatusCodeProvider + */ + public function testServerExceptionFromKnownStatusCodes(int $statusCode, string $expectedText): void + { + $response = new Response($statusCode, []); + + $exception = ServerException::fromServerErrorResponse($response); + + $this->assertSame($statusCode, $exception->getCode()); + $this->assertStringContainsString($expectedText, $exception->getMessage()); + $this->assertStringContainsString((string) $statusCode, $exception->getMessage()); + } + + /** + * @return array + */ + public static function knownServerStatusCodeProvider(): array + { + return [ + '500 Internal Server Error' => [500, 'Internal Server Error'], + '502 Bad Gateway' => [502, 'Bad Gateway'], + '503 Service Unavailable' => [503, 'Service Unavailable'], + '504 Gateway Timeout' => [504, 'Gateway Timeout'], + '507 Insufficient Storage' => [507, 'Insufficient Storage'], + '529 Overloaded' => [529, 'Overloaded'], + ]; + } + + public function testServerExceptionFromUnknownStatusCode(): void + { + $response = new Response(599, []); + + $exception = ServerException::fromServerErrorResponse($response); + + $this->assertSame(599, $exception->getCode()); + $this->assertStringContainsString('Server error (599)', $exception->getMessage()); + $this->assertStringContainsString('server-side issue', $exception->getMessage()); + } + + public function testServerExceptionExtractsErrorMessageFromResponseBody(): void + { + $body = json_encode(['error' => ['message' => 'The server is overloaded']]); + $response = new Response(500, [], $body); + + $exception = ServerException::fromServerErrorResponse($response); + + $this->assertStringContainsString('Internal Server Error (500)', $exception->getMessage()); + $this->assertStringContainsString('The server is overloaded', $exception->getMessage()); + } + + public function testServerExceptionExtractsStringErrorFromResponseBody(): void + { + $body = json_encode(['error' => 'Something went wrong']); + $response = new Response(502, [], $body); + + $exception = ServerException::fromServerErrorResponse($response); + + $this->assertStringContainsString('Bad Gateway (502)', $exception->getMessage()); + $this->assertStringContainsString('Something went wrong', $exception->getMessage()); + } + + public function testServerExceptionExtractsMessageKeyFromResponseBody(): void + { + $body = json_encode(['message' => 'Service temporarily unavailable']); + $response = new Response(503, [], $body); + + $exception = ServerException::fromServerErrorResponse($response); + + $this->assertStringContainsString('Service Unavailable (503)', $exception->getMessage()); + $this->assertStringContainsString('Service temporarily unavailable', $exception->getMessage()); + } + + public function testServerExceptionWithNoBody(): void + { + $response = new Response(500, []); + + $exception = ServerException::fromServerErrorResponse($response); + + $this->assertSame('Internal Server Error (500)', $exception->getMessage()); + } + + public function testServerExceptionWithNonJsonBody(): void + { + $response = new Response(500, [], 'Server Error'); + + $exception = ServerException::fromServerErrorResponse($response); + + $this->assertSame('Internal Server Error (500)', $exception->getMessage()); + } }