Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Providers/Http/Exception/ServerException.php
Original file line number Diff line number Diff line change
Expand Up @@ -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])) {
Expand Down
103 changes: 103 additions & 0 deletions tests/unit/Exceptions/ExceptionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
{
Expand All @@ -32,6 +35,7 @@ public function testAllExceptionsImplementAiClientExceptionInterface(): void
new TokenLimitReachedException('test'),
new NetworkException('test'),
new ClientException('test'),
new ServerException('test'),
];

foreach ($exceptions as $exception) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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<string, array{int, string}>
*/
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, [], '<html>Server Error</html>');

$exception = ServerException::fromServerErrorResponse($response);

$this->assertSame('Internal Server Error (500)', $exception->getMessage());
}
}