Skip to content

Commit 376c838

Browse files
committed
Update chat unit test to include different test cases
1 parent bf188d4 commit 376c838

File tree

1 file changed

+188
-1
lines changed

1 file changed

+188
-1
lines changed

Tests/OpenAIProvider/ChatTest.php

Lines changed: 188 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
<?php
22

3+
use Joomla\AI\Exception\AuthenticationException;
4+
use Joomla\AI\Exception\ProviderException;
5+
use Joomla\AI\Exception\RateLimitException;
6+
use Joomla\AI\Exception\UnserializableResponseException;
37
use Joomla\Http\Http;
48
use Joomla\Http\HttpFactory;
59
use Joomla\AI\Provider\OpenAIProvider;
@@ -10,7 +14,7 @@ class ChatTest extends TestCase
1014
{
1115
public function testSimpleChatCompletion()
1216
{
13-
echo "Test 1: Test chat method with a simple prompt\n";
17+
echo "Test 1: Test chat method for successful completion\n";
1418

1519
$fakeChatResponseBody = json_encode([
1620
'id' => 'chatcmpl-test',
@@ -59,4 +63,187 @@ public function testSimpleChatCompletion()
5963
$this->assertArrayHasKey('model', $metadata);
6064
$this->assertArrayHasKey('usage', $metadata);
6165
}
66+
67+
public function testChatRaisesProviderException()
68+
{
69+
echo "Test 2: Test chat method raises ProviderException on server error\n";
70+
71+
$httpFactoryMock = $this->createMock(HttpFactory::class);
72+
$httpClientMock = $this->createMock(Http::class);
73+
$httpFactoryMock->method('getHttp')->with([])->willReturn($httpClientMock);
74+
75+
$moderationResponse = $this->createJsonResponse([
76+
'id' => 'modr-test',
77+
'model' => 'text-moderation-001',
78+
'results' => [['flagged' => false]],
79+
]);
80+
81+
$serverErrorResponse = $this->createJsonResponse([
82+
'error' => [
83+
'message' => 'Internal server error',
84+
'type' => 'server_error',
85+
],
86+
], 500);
87+
88+
$httpClientMock->method('post')->willReturnOnConsecutiveCalls($moderationResponse, $serverErrorResponse);
89+
90+
$provider = new OpenAIProvider(['api_key' => 'test-api-key'], $httpFactoryMock);
91+
92+
$this->expectException(ProviderException::class);
93+
$this->expectExceptionMessage('Internal server error');
94+
$provider->chat('Hello!', ['model' => 'gpt-4o-mini']);
95+
}
96+
97+
public function testChatRaisesAuthenticationException()
98+
{
99+
echo "Test 3: Test chat method raises AuthenticationException on unauthorized access\n";
100+
101+
$httpFactoryMock = $this->createMock(HttpFactory::class);
102+
$httpClientMock = $this->createMock(Http::class);
103+
$httpFactoryMock->method('getHttp')->with([])->willReturn($httpClientMock);
104+
105+
$moderationResponse = $this->createJsonResponse([
106+
'id' => 'modr-test',
107+
'model' => 'text-moderation-001',
108+
'results' => [['flagged' => false]],
109+
]);
110+
111+
$unauthorizedResponse = $this->createJsonResponse([
112+
'error' => [
113+
'message' => 'Incorrect API key provided',
114+
'type' => 'invalid_request_error',
115+
'code' => 'invalid_api_key',
116+
],
117+
], 401);
118+
119+
$httpClientMock->method('post')->willReturnOnConsecutiveCalls($moderationResponse, $unauthorizedResponse);
120+
121+
$provider = new OpenAIProvider(['api_key' => 'bad-key'], $httpFactoryMock);
122+
123+
$this->expectException(AuthenticationException::class);
124+
$this->expectExceptionMessage('Incorrect API key provided');
125+
$provider->chat('Hello!', ['model' => 'gpt-4o-mini']);
126+
}
127+
128+
public function testChatRaisesRateLimitException()
129+
{
130+
echo "Test 4: Test chat method raises RateLimitException on too many requests\n";
131+
132+
$httpFactoryMock = $this->createMock(HttpFactory::class);
133+
$httpClientMock = $this->createMock(Http::class);
134+
$httpFactoryMock->method('getHttp')->with([])->willReturn($httpClientMock);
135+
136+
$moderationResponse = $this->createJsonResponse([
137+
'id' => 'modr-test',
138+
'model' => 'text-moderation-001',
139+
'results' => [['flagged' => false]],
140+
]);
141+
142+
$rateLimitResponse = $this->createJsonResponse([
143+
'error' => [
144+
'message' => 'Rate limit exceeded',
145+
'type' => 'rate_limit_error',
146+
],
147+
], 429);
148+
149+
$httpClientMock->method('post')->willReturnOnConsecutiveCalls($moderationResponse, $rateLimitResponse);
150+
151+
$provider = new OpenAIProvider(['api_key' => 'test-api-key'], $httpFactoryMock);
152+
153+
$this->expectException(RateLimitException::class);
154+
$this->expectExceptionMessage('Rate limit exceeded');
155+
$provider->chat('Hello!', ['model' => 'gpt-4o-mini']);
156+
}
157+
158+
public function testChatRaisesUnserializableResponse()
159+
{
160+
echo "Test 5: Test chat method raises UnserializableResponseException on invalid JSON\n";
161+
162+
$httpFactoryMock = $this->createMock(HttpFactory::class);
163+
$httpClientMock = $this->createMock(Http::class);
164+
$httpFactoryMock->method('getHttp')->with([])->willReturn($httpClientMock);
165+
166+
$moderationResponse = $this->createJsonResponse([
167+
'id' => 'modr-test',
168+
'model' => 'text-moderation-001',
169+
'results' => [['flagged' => false]],
170+
]);
171+
172+
$invalidJson = new HttpResponse('php://memory', 200, ['Content-Type' => 'application/json']);
173+
$invalidJson->getBody()->write('{ not valid json');
174+
175+
$httpClientMock->method('post')->willReturnOnConsecutiveCalls($moderationResponse, $invalidJson);
176+
177+
$provider = new OpenAIProvider(['api_key' => 'test-api-key'], $httpFactoryMock);
178+
179+
$this->expectException(UnserializableResponseException::class);
180+
$this->expectExceptionMessage('Syntax error');
181+
$provider->chat('Hello!', ['model' => 'gpt-4o-mini']);
182+
}
183+
184+
public function testChatHandlesBase64ContentInChoices()
185+
{
186+
echo "Test 6: Test chat method handles base64 encoded content in choices metadata\n";
187+
188+
$rawAudio = 'fake-audio-bytes';
189+
$encodedAudio = base64_encode($rawAudio);
190+
191+
$moderationResponse = $this->createJsonResponse([
192+
'id' => 'modr-test',
193+
'model' => 'text-moderation-001',
194+
'results' => [['flagged' => false]],
195+
]);
196+
197+
$chatResponse = $this->createJsonResponse([
198+
'id' => 'chatcmpl-audio',
199+
'object' => 'chat.completion',
200+
'created' => time(),
201+
'model' => 'gpt-4o-mini',
202+
'usage' => [
203+
'prompt_tokens' => 9,
204+
'completion_tokens' => 12,
205+
'total_tokens' => 21,
206+
],
207+
'choices' => [
208+
[
209+
'index' => 0,
210+
'message' => [
211+
'role' => 'assistant',
212+
'content' => $encodedAudio,
213+
'mime_type' => 'audio/wav',
214+
],
215+
'finish_reason' => 'stop',
216+
],
217+
],
218+
]);
219+
220+
$httpFactoryMock = $this->createMock(HttpFactory::class);
221+
$httpClientMock = $this->createMock(Http::class);
222+
$httpFactoryMock->method('getHttp')->with([])->willReturn($httpClientMock);
223+
$httpClientMock->method('post')->willReturnOnConsecutiveCalls($moderationResponse, $chatResponse);
224+
225+
$provider = new OpenAIProvider(['api_key' => 'test-api-key'], $httpFactoryMock);
226+
227+
$response = $provider->chat('Please return audio as base64.', ['model' => 'gpt-4o-mini']);
228+
229+
$this->assertSame($encodedAudio, $response->getContent());
230+
231+
$metadata = $response->getMetadata();
232+
$this->assertSame('gpt-4o-mini', $metadata['model']);
233+
$this->assertSame($encodedAudio, $metadata['choices'][0]['message']['content']);
234+
$this->assertSame('audio/wav', $metadata['choices'][0]['message']['mime_type']);
235+
236+
$decoded = base64_decode($response->getContent(), true);
237+
$this->assertNotFalse($decoded);
238+
$this->assertSame($rawAudio, $decoded);
239+
}
240+
241+
private function createJsonResponse(array $payload, int $status = 200): HttpResponse
242+
{
243+
$response = new HttpResponse('php://memory', $status, ['Content-Type' => 'application/json']);
244+
$stream = $response->getBody();
245+
$stream->write(json_encode($payload));
246+
247+
return $response;
248+
}
62249
}

0 commit comments

Comments
 (0)