Skip to content

Commit 0cfa823

Browse files
committed
fix: add throwIfNotSuccessfulStatusCode method to validate HTTP status codes
1 parent 6be1fb8 commit 0cfa823

File tree

3 files changed

+208
-0
lines changed

3 files changed

+208
-0
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenAI\Exceptions;
6+
7+
use Exception;
8+
9+
final class UnexpectedStatusCodeException extends Exception
10+
{
11+
/**
12+
* Creates a new Exception instance.
13+
*
14+
* @param int $statusCode The HTTP status code.
15+
*/
16+
17+
public function __construct(int $statusCode)
18+
{
19+
$message = "Unexpected status code: {$statusCode}";
20+
21+
parent::__construct($message, $statusCode);
22+
}
23+
}

src/Transporters/HttpTransporter.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use OpenAI\Enums\Transporter\ContentType;
1212
use OpenAI\Exceptions\ErrorException;
1313
use OpenAI\Exceptions\TransporterException;
14+
use OpenAI\Exceptions\UnexpectedStatusCodeException;
1415
use OpenAI\Exceptions\UnserializableResponse;
1516
use OpenAI\ValueObjects\Transporter\BaseUri;
1617
use OpenAI\ValueObjects\Transporter\Headers;
@@ -48,6 +49,8 @@ public function requestObject(Payload $payload): Response
4849

4950
$response = $this->sendRequest(fn (): \Psr\Http\Message\ResponseInterface => $this->client->sendRequest($request));
5051

52+
$this->throwIfNotSuccessfulStatusCode($response);
53+
5154
$contents = (string) $response->getBody();
5255

5356
if (str_contains($response->getHeaderLine('Content-Type'), ContentType::TEXT_PLAIN->value)) {
@@ -75,6 +78,8 @@ public function requestContent(Payload $payload): string
7578

7679
$response = $this->sendRequest(fn (): \Psr\Http\Message\ResponseInterface => $this->client->sendRequest($request));
7780

81+
$this->throwIfNotSuccessfulStatusCode($response);
82+
7883
$contents = (string) $response->getBody();
7984

8085
$this->throwIfJsonError($response, $contents);
@@ -91,6 +96,8 @@ public function requestStream(Payload $payload): ResponseInterface
9196

9297
$response = $this->sendRequest(fn () => ($this->streamHandler)($request));
9398

99+
$this->throwIfNotSuccessfulStatusCode($response);
100+
94101
$this->throwIfJsonError($response, $response);
95102

96103
return $response;
@@ -136,4 +143,13 @@ private function throwIfJsonError(ResponseInterface $response, string|ResponseIn
136143
throw new UnserializableResponse($jsonException);
137144
}
138145
}
146+
147+
private function throwIfNotSuccessfulStatusCode(ResponseInterface $response): void
148+
{
149+
$statusCode = $response->getStatusCode();
150+
151+
if ($statusCode < 200 || $statusCode >= 300) {
152+
throw new UnexpectedStatusCodeException($statusCode);
153+
}
154+
}
139155
}

tests/Transporters/HttpTransporter.php

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Psr\Http\Client\ClientInterface;
1717
use Psr\Http\Message\RequestInterface;
1818
use Psr\Http\Message\ResponseInterface;
19+
use \OpenAI\Exceptions\UnexpectedStatusCodeException;
1920

2021
beforeEach(function () {
2122
$this->client = Mockery::mock(ClientInterface::class);
@@ -531,3 +532,171 @@
531532
->and($e->getErrorType())->toBe('invalid_request_error');
532533
});
533534
});
535+
536+
test('request stream client error 400', function () {
537+
$payload = Payload::create('completions', []);
538+
539+
$response = new Response(400, [], json_encode([
540+
'error' => [
541+
'message' => 'Bad Request',
542+
'type' => 'client_error',
543+
'param' => null,
544+
'code' => 'bad_request',
545+
],
546+
]));
547+
548+
$this->client
549+
->shouldReceive('sendAsyncRequest')
550+
->once()
551+
->andReturn($response);
552+
553+
expect(fn () => $this->http->requestStream($payload))
554+
->toThrow(function (UnexpectedStatusCodeException $e) {
555+
expect($e->getMessage())->toBe('Unexpected status code: 400')
556+
->and($e->getCode())->toBe(400);
557+
});
558+
});
559+
560+
test('request stream client error 401', function () {
561+
$payload = Payload::create('completions', []);
562+
563+
$response = new Response(401, [], json_encode([
564+
'error' => [
565+
'message' => 'Unauthorized',
566+
'type' => 'client_error',
567+
'param' => null,
568+
'code' => 'unauthorized',
569+
],
570+
]));
571+
572+
$this->client
573+
->shouldReceive('sendAsyncRequest')
574+
->once()
575+
->andReturn($response);
576+
577+
expect(fn () => $this->http->requestStream($payload))
578+
->toThrow(function (UnexpectedStatusCodeException $e) {
579+
expect($e->getMessage())->toBe('Unexpected status code: 401')
580+
->and($e->getCode())->toBe(401);
581+
});
582+
});
583+
584+
test('request stream client error 403', function () {
585+
$payload = Payload::create('completions', []);
586+
587+
$response = new Response(403, [], json_encode([
588+
'error' => [
589+
'message' => 'Forbidden',
590+
'type' => 'client_error',
591+
'param' => null,
592+
'code' => 'forbidden',
593+
],
594+
]));
595+
596+
$this->client
597+
->shouldReceive('sendAsyncRequest')
598+
->once()
599+
->andReturn($response);
600+
601+
expect(fn () => $this->http->requestStream($payload))
602+
->toThrow(function (UnexpectedStatusCodeException $e) {
603+
expect($e->getMessage())->toBe('Unexpected status code: 403')
604+
->and($e->getCode())->toBe(403);
605+
});
606+
});
607+
608+
test('request stream client error 404', function () {
609+
$payload = Payload::create('completions', []);
610+
611+
$response = new Response(404, [], json_encode([
612+
'error' => [
613+
'message' => 'Not Found',
614+
'type' => 'client_error',
615+
'param' => null,
616+
'code' => 'not_found',
617+
],
618+
]));
619+
620+
$this->client
621+
->shouldReceive('sendAsyncRequest')
622+
->once()
623+
->andReturn($response);
624+
625+
expect(fn () => $this->http->requestStream($payload))
626+
->toThrow(function (UnexpectedStatusCodeException $e) {
627+
expect($e->getMessage())->toBe('Unexpected status code: 404')
628+
->and($e->getCode())->toBe(404);
629+
});
630+
});
631+
632+
test('request stream client error 422', function () {
633+
$payload = Payload::create('completions', []);
634+
635+
$response = new Response(422, [], json_encode([
636+
'error' => [
637+
'message' => 'Unprocessable Entity',
638+
'type' => 'client_error',
639+
'param' => null,
640+
'code' => 'unprocessable_entity',
641+
],
642+
]));
643+
644+
$this->client
645+
->shouldReceive('sendAsyncRequest')
646+
->once()
647+
->andReturn($response);
648+
649+
expect(fn () => $this->http->requestStream($payload))
650+
->toThrow(function (UnexpectedStatusCodeException $e) {
651+
expect($e->getMessage())->toBe('Unexpected status code: 422')
652+
->and($e->getCode())->toBe(422);
653+
});
654+
});
655+
656+
test('request stream client error 429', function () {
657+
$payload = Payload::create('completions', []);
658+
659+
$response = new Response(429, [], json_encode([
660+
'error' => [
661+
'message' => 'Too Many Requests',
662+
'type' => 'client_error',
663+
'param' => null,
664+
'code' => 'too_many_requests',
665+
],
666+
]));
667+
668+
$this->client
669+
->shouldReceive('sendAsyncRequest')
670+
->once()
671+
->andReturn($response);
672+
673+
expect(fn () => $this->http->requestStream($payload))
674+
->toThrow(function (UnexpectedStatusCodeException $e) {
675+
expect($e->getMessage())->toBe('Unexpected status code: 429')
676+
->and($e->getCode())->toBe(429);
677+
});
678+
});
679+
680+
test('request stream client error 500', function () {
681+
$payload = Payload::create('completions', []);
682+
683+
$response = new Response(500, [], json_encode([
684+
'error' => [
685+
'message' => 'Internal Server Error',
686+
'type' => 'client_error',
687+
'param' => null,
688+
'code' => 'internal_server_error',
689+
],
690+
]));
691+
692+
$this->client
693+
->shouldReceive('sendAsyncRequest')
694+
->once()
695+
->andReturn($response);
696+
697+
expect(fn () => $this->http->requestStream($payload))
698+
->toThrow(function (UnexpectedStatusCodeException $e) {
699+
expect($e->getMessage())->toBe('Unexpected status code: 500')
700+
->and($e->getCode())->toBe(500);
701+
});
702+
});

0 commit comments

Comments
 (0)