Skip to content
This repository was archived by the owner on Jul 16, 2025. It is now read-only.

Commit a6370de

Browse files
committed
feat: Add comprehensive unit tests for Albert API integration
- Add unit tests for EmbeddingsModelClient and GPTModelClient - Update PlatformFactory to require explicit API version in URL - Replace webmozart/assert with plain PHP exception handling - Use JsonMockResponse and TestWith attributes with descriptions in tests - Fix URL handling to ensure proper slash between version and endpoint - Apply rector and code style fixes
1 parent 1217e51 commit a6370de

File tree

4 files changed

+27
-87
lines changed

4 files changed

+27
-87
lines changed

src/Platform/Bridge/Albert/GPTModelClient.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818

1919
public function __construct(
2020
HttpClientInterface $httpClient,
21-
#[\SensitiveParameter]
22-
private string $apiKey,
21+
#[\SensitiveParameter] private string $apiKey,
2322
private string $baseUrl,
2423
) {
2524
$this->httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);

tests/Platform/Bridge/Albert/EmbeddingsModelClientTest.php

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\GPT;
1010
use PhpLlm\LlmChain\Platform\Exception\InvalidArgumentException;
1111
use PHPUnit\Framework\Attributes\CoversClass;
12-
use PHPUnit\Framework\Attributes\DataProvider;
1312
use PHPUnit\Framework\Attributes\Small;
1413
use PHPUnit\Framework\Attributes\Test;
14+
use PHPUnit\Framework\Attributes\TestWith;
1515
use PHPUnit\Framework\TestCase;
1616
use Symfony\Component\HttpClient\MockHttpClient;
1717
use Symfony\Component\HttpClient\Response\JsonMockResponse;
@@ -73,7 +73,10 @@ public function doesNotSupportNonEmbeddingsModel(): void
7373
}
7474

7575
#[Test]
76-
#[DataProvider('requestDataProvider')]
76+
#[TestWith([['input' => 'test text', 'model' => 'text-embedding-ada-002'], [], ['input' => 'test text', 'model' => 'text-embedding-ada-002']], 'with array payload and no options')]
77+
#[TestWith(['test text', [], 'test text'], 'with string payload and no options')]
78+
#[TestWith([['input' => 'test text', 'model' => 'text-embedding-ada-002'], ['dimensions' => 1536], ['dimensions' => 1536, 'input' => 'test text', 'model' => 'text-embedding-ada-002']], 'with array payload and options')]
79+
#[TestWith([['input' => 'test text', 'model' => 'text-embedding-ada-002'], ['model' => 'text-embedding-3-small'], ['model' => 'text-embedding-3-small', 'input' => 'test text']], 'options override payload values')]
7780
public function requestSendsCorrectHttpRequest(array|string $payload, array $options, array|string $expectedJson): void
7881
{
7982
$capturedRequest = null;
@@ -108,32 +111,6 @@ public function requestSendsCorrectHttpRequest(array|string $payload, array $opt
108111
}
109112
}
110113

111-
public static function requestDataProvider(): \Iterator
112-
{
113-
yield 'with array payload and no options' => [
114-
'payload' => ['input' => 'test text', 'model' => 'text-embedding-ada-002'],
115-
'options' => [],
116-
'expectedJson' => ['input' => 'test text', 'model' => 'text-embedding-ada-002'],
117-
];
118-
119-
yield 'with string payload and no options' => [
120-
'payload' => 'test text',
121-
'options' => [],
122-
'expectedJson' => 'test text',
123-
];
124-
125-
yield 'with array payload and options' => [
126-
'payload' => ['input' => 'test text', 'model' => 'text-embedding-ada-002'],
127-
'options' => ['dimensions' => 1536],
128-
'expectedJson' => ['dimensions' => 1536, 'input' => 'test text', 'model' => 'text-embedding-ada-002'],
129-
];
130-
131-
yield 'options override payload values' => [
132-
'payload' => ['input' => 'test text', 'model' => 'text-embedding-ada-002'],
133-
'options' => ['model' => 'text-embedding-3-small'],
134-
'expectedJson' => ['model' => 'text-embedding-3-small', 'input' => 'test text'],
135-
];
136-
}
137114

138115
#[Test]
139116
public function requestHandlesBaseUrlWithoutTrailingSlash(): void

tests/Platform/Bridge/Albert/GPTModelClientTest.php

Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\GPT;
1010
use PhpLlm\LlmChain\Platform\Exception\InvalidArgumentException;
1111
use PHPUnit\Framework\Attributes\CoversClass;
12-
use PHPUnit\Framework\Attributes\DataProvider;
1312
use PHPUnit\Framework\Attributes\Small;
1413
use PHPUnit\Framework\Attributes\Test;
14+
use PHPUnit\Framework\Attributes\TestWith;
1515
use PHPUnit\Framework\TestCase;
1616
use Symfony\Component\HttpClient\EventSourceHttpClient;
1717
use Symfony\Component\HttpClient\MockHttpClient;
@@ -118,7 +118,11 @@ public function doesNotSupportNonGPTModel(): void
118118
}
119119

120120
#[Test]
121-
#[DataProvider('requestDataProvider')]
121+
#[TestWith([['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo'], [], ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo']], 'with array payload and no options')]
122+
#[TestWith(['test message', [], 'test message'], 'with string payload and no options')]
123+
#[TestWith([['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo'], ['temperature' => 0.7, 'max_tokens' => 150], ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo', 'temperature' => 0.7, 'max_tokens' => 150]], 'with array payload and options')]
124+
#[TestWith([['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo', 'temperature' => 1.0], ['temperature' => 0.5], ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo', 'temperature' => 0.5]], 'options override payload values')]
125+
#[TestWith([['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo'], ['stream' => true], ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo', 'stream' => true]], 'with streaming option')]
122126
public function requestSendsCorrectHttpRequest(array|string $payload, array $options, array|string $expectedJson): void
123127
{
124128
$capturedRequest = null;
@@ -153,38 +157,6 @@ public function requestSendsCorrectHttpRequest(array|string $payload, array $opt
153157
}
154158
}
155159

156-
public static function requestDataProvider(): \Iterator
157-
{
158-
yield 'with array payload and no options' => [
159-
'payload' => ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo'],
160-
'options' => [],
161-
'expectedJson' => ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo'],
162-
];
163-
164-
yield 'with string payload and no options' => [
165-
'payload' => 'test message',
166-
'options' => [],
167-
'expectedJson' => 'test message',
168-
];
169-
170-
yield 'with array payload and options' => [
171-
'payload' => ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo'],
172-
'options' => ['temperature' => 0.7, 'max_tokens' => 150],
173-
'expectedJson' => ['temperature' => 0.7, 'max_tokens' => 150, 'messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo'],
174-
];
175-
176-
yield 'options override payload values' => [
177-
'payload' => ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo', 'temperature' => 1.0],
178-
'options' => ['temperature' => 0.5],
179-
'expectedJson' => ['temperature' => 0.5, 'messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo'],
180-
];
181-
182-
yield 'with streaming option' => [
183-
'payload' => ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo'],
184-
'options' => ['stream' => true],
185-
'expectedJson' => ['stream' => true, 'messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo'],
186-
];
187-
}
188160

189161
#[Test]
190162
public function requestHandlesBaseUrlWithoutTrailingSlash(): void

tests/Platform/Bridge/Albert/PlatformFactoryTest.php

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
use PhpLlm\LlmChain\Platform\Exception\InvalidArgumentException;
99
use PhpLlm\LlmChain\Platform\Platform;
1010
use PHPUnit\Framework\Attributes\CoversClass;
11-
use PHPUnit\Framework\Attributes\DataProvider;
1211
use PHPUnit\Framework\Attributes\Small;
1312
use PHPUnit\Framework\Attributes\Test;
13+
use PHPUnit\Framework\Attributes\TestWith;
1414
use PHPUnit\Framework\TestCase;
1515

1616
#[CoversClass(PlatformFactory::class)]
@@ -26,24 +26,20 @@ public function createsPlatformWithCorrectBaseUrl(): void
2626
}
2727

2828
#[Test]
29-
#[DataProvider('urlProvider')]
29+
#[TestWith(['https://albert.example.com/v1'], 'with v1 path')]
30+
#[TestWith(['https://albert.example.com/v1/'], 'with v1 path and trailing slash')]
31+
#[TestWith(['https://albert.example.com/v2'], 'with v2 path')]
32+
#[TestWith(['https://albert.example.com/v2/'], 'with v2 path and trailing slash')]
33+
#[TestWith(['https://albert.example.com/v3'], 'with v3 path')]
34+
#[TestWith(['https://albert.example.com/v10'], 'with v10 path')]
35+
#[TestWith(['https://albert.example.com/v99'], 'with v99 path')]
3036
public function handlesUrlsCorrectly(string $url): void
3137
{
3238
$platform = PlatformFactory::create('test-key', $url);
3339

3440
self::assertInstanceOf(Platform::class, $platform);
3541
}
3642

37-
public static function urlProvider(): \Iterator
38-
{
39-
yield 'with v1 path' => ['https://albert.example.com/v1'];
40-
yield 'with v1 path and trailing slash' => ['https://albert.example.com/v1/'];
41-
yield 'with v2 path' => ['https://albert.example.com/v2'];
42-
yield 'with v2 path and trailing slash' => ['https://albert.example.com/v2/'];
43-
yield 'with v3 path' => ['https://albert.example.com/v3'];
44-
yield 'with v10 path' => ['https://albert.example.com/v10'];
45-
yield 'with v99 path' => ['https://albert.example.com/v99'];
46-
}
4743

4844
#[Test]
4945
public function throwsExceptionForNonHttpsUrl(): void
@@ -55,7 +51,13 @@ public function throwsExceptionForNonHttpsUrl(): void
5551
}
5652

5753
#[Test]
58-
#[DataProvider('urlsWithoutVersionProvider')]
54+
#[TestWith(['https://albert.example.com'], 'without version')]
55+
#[TestWith(['https://albert.example.com/'], 'with trailing slash only')]
56+
#[TestWith(['https://albert.example.com/vx'], 'with vx path')]
57+
#[TestWith(['https://albert.example.com/version'], 'with version path')]
58+
#[TestWith(['https://albert.example.com/api'], 'with api path')]
59+
#[TestWith(['https://albert.example.com/v'], 'with v path only')]
60+
#[TestWith(['https://albert.example.com/v-1'], 'with v- path')]
5961
public function throwsExceptionForUrlsWithoutVersion(string $url): void
6062
{
6163
$this->expectException(InvalidArgumentException::class);
@@ -64,14 +66,4 @@ public function throwsExceptionForUrlsWithoutVersion(string $url): void
6466
PlatformFactory::create('test-key', $url);
6567
}
6668

67-
public static function urlsWithoutVersionProvider(): \Iterator
68-
{
69-
yield 'without version' => ['https://albert.example.com'];
70-
yield 'with trailing slash only' => ['https://albert.example.com/'];
71-
yield 'with vx path' => ['https://albert.example.com/vx'];
72-
yield 'with version path' => ['https://albert.example.com/version'];
73-
yield 'with api path' => ['https://albert.example.com/api'];
74-
yield 'with v path only' => ['https://albert.example.com/v'];
75-
yield 'with v- path' => ['https://albert.example.com/v-1'];
76-
}
7769
}

0 commit comments

Comments
 (0)