Skip to content

Commit c60fafb

Browse files
committed
Add phpUnit unit tests for BuerklinProvider
1 parent b7f6274 commit c60fafb

File tree

1 file changed

+287
-0
lines changed

1 file changed

+287
-0
lines changed
Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Tests\Services\InfoProviderSystem\Providers;
6+
7+
use App\Services\InfoProviderSystem\Providers\BuerklinProvider;
8+
use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
9+
use App\Services\InfoProviderSystem\DTOs\SearchResultDTO;
10+
use App\Settings\InfoProviderSystem\BuerklinSettings;
11+
use PHPUnit\Framework\TestCase;
12+
use Symfony\Contracts\HttpClient\HttpClientInterface;
13+
use Symfony\Contracts\HttpClient\ResponseInterface;
14+
use Psr\Cache\CacheItemPoolInterface;
15+
use Psr\Cache\CacheItemInterface;
16+
use App\Services\OAuth\OAuthTokenManager;
17+
18+
/**
19+
* Full behavioral test suite for BuerklinProvider.
20+
* Includes parameter parsing, compliance parsing, images, prices and batch mode.
21+
*/
22+
class BuerklinProviderTest extends TestCase
23+
{
24+
private HttpClientInterface $httpClient;
25+
private OAuthTokenManager $tokenManager;
26+
private CacheItemPoolInterface $cache;
27+
private BuerklinSettings $settings;
28+
private BuerklinProvider $provider;
29+
30+
protected function setUp(): void
31+
{
32+
$this->httpClient = $this->createMock(HttpClientInterface::class);
33+
$this->tokenManager = $this->createMock(OAuthTokenManager::class);
34+
35+
// Cache mock
36+
$cacheItem = $this->createMock(CacheItemInterface::class);
37+
$cacheItem->method('isHit')->willReturn(false);
38+
$cacheItem->method('set')->willReturn($cacheItem);
39+
40+
$this->cache = $this->createMock(CacheItemPoolInterface::class);
41+
$this->cache->method('getItem')->willReturn($cacheItem);
42+
43+
$this->settings = new BuerklinSettings();
44+
$this->settings->clientId = 'CID';
45+
$this->settings->secret = 'SECRET';
46+
$this->settings->username = 'USER';
47+
$this->settings->password = 'PASS';
48+
$this->settings->language = 'en';
49+
$this->settings->currency = 'EUR';
50+
51+
$this->provider = new BuerklinProvider(
52+
client: $this->httpClient,
53+
authTokenManager: $this->tokenManager,
54+
partInfoCache: $this->cache,
55+
settings: $this->settings,
56+
);
57+
}
58+
59+
private function mockApi(string $expectedUrl, array $jsonResponse): void
60+
{
61+
$response = $this->createMock(ResponseInterface::class);
62+
$response->method('toArray')->willReturn($jsonResponse);
63+
64+
$this->httpClient
65+
->method('request')
66+
->with(
67+
'GET',
68+
$this->callback(fn($url) => str_contains($url, $expectedUrl)),
69+
$this->anything()
70+
)
71+
->willReturn($response);
72+
}
73+
74+
// ---------------------------------------------------------
75+
// Test: attributesToParameters
76+
// ---------------------------------------------------------
77+
public function testAttributesToParametersParsesUnitsAndValues(): void
78+
{
79+
$method = new \ReflectionMethod(BuerklinProvider::class, 'attributesToParameters');
80+
$method->setAccessible(true);
81+
82+
$features = [
83+
[
84+
'name' => 'Zener voltage',
85+
'featureUnit' => ['symbol' => 'V'],
86+
'featureValues' => [
87+
['value' => '12']
88+
]
89+
],
90+
[
91+
'name' => 'Length',
92+
'featureUnit' => ['symbol' => 'mm'],
93+
'featureValues' => [
94+
['value' => '2.9']
95+
]
96+
],
97+
[
98+
'name' => 'Assembly',
99+
'featureUnit' => [],
100+
'featureValues' => [
101+
['value' => 'SMD']
102+
]
103+
]
104+
];
105+
106+
$params = $method->invoke($this->provider, $features, '');
107+
108+
$this->assertCount(3, $params);
109+
110+
$this->assertSame('Zener voltage', $params[0]->name);
111+
$this->assertSame('12', $params[0]->value_text);
112+
$this->assertSame('V', $params[0]->unit);
113+
114+
$this->assertSame('Length', $params[1]->name);
115+
$this->assertSame('2.9', $params[1]->value_text);
116+
$this->assertSame('mm', $params[1]->unit);
117+
118+
$this->assertSame('Assembly', $params[2]->name);
119+
$this->assertSame('SMD', $params[2]->value_text);
120+
$this->assertNull($params[2]->unit);
121+
}
122+
123+
// ---------------------------------------------------------
124+
// Test: complianceToParameters
125+
// ---------------------------------------------------------
126+
public function testComplianceParameters(): void
127+
{
128+
$method = new \ReflectionMethod(BuerklinProvider::class, 'complianceToParameters');
129+
$method->setAccessible(true);
130+
131+
$product = [
132+
'labelRoHS' => 'Yes',
133+
'dateRoHS' => '2015-03-31T00:00+0000',
134+
'SVHC' => true,
135+
'hazardousGood' => false,
136+
'hazardousMaterials' => false,
137+
'countryOfOrigin' => 'China',
138+
'articleCustomsCode' => '85411000'
139+
];
140+
141+
$params = $method->invoke($this->provider, $product, 'Compliance');
142+
143+
$map = [];
144+
foreach ($params as $p) {
145+
$map[$p->name] = $p->value_text;
146+
}
147+
148+
$this->assertSame('Yes', $map['RoHS conform']);
149+
$this->assertSame('2015-03-31', $map['RoHS date']);
150+
$this->assertSame('Yes', $map['SVHC free']);
151+
$this->assertSame('No', $map['Hazardous good']);
152+
$this->assertSame('No', $map['Hazardous materials']);
153+
$this->assertSame('China', $map['Country of origin']);
154+
$this->assertSame('85411000', $map['Customs code']);
155+
}
156+
157+
// ---------------------------------------------------------
158+
// Test: image handling
159+
// ---------------------------------------------------------
160+
public function testImageSelectionPrefersZoomAndDeduplicates(): void
161+
{
162+
$method = new \ReflectionMethod(BuerklinProvider::class, 'getProductImages');
163+
$method->setAccessible(true);
164+
165+
$images = [
166+
['format' => 'product', 'url' => '/img/a.webp'],
167+
['format' => 'zoom', 'url' => '/img/z.webp'],
168+
['format' => 'zoom', 'url' => '/img/z.webp'], // dup
169+
['format' => 'thumbnail', 'url' => '/img/t.webp']
170+
];
171+
172+
$results = $method->invoke($this->provider, $images);
173+
174+
$this->assertCount(1, $results);
175+
$this->assertSame('https://www.buerklin.com/img/z.webp', $results[0]->url);
176+
}
177+
178+
// ---------------------------------------------------------
179+
// Test: footprint extraction
180+
// ---------------------------------------------------------
181+
public function testFootprintExtraction(): void
182+
{
183+
$method = new \ReflectionMethod(BuerklinProvider::class, 'getPartDetail');
184+
$method->setAccessible(true);
185+
186+
$product = [
187+
'code' => 'TEST1',
188+
'manufacturerProductId' => 'ABC',
189+
'description' => 'X',
190+
'images' => [],
191+
'classifications' => [
192+
[
193+
'name' => 'Cat',
194+
'features' => [
195+
[
196+
'name' => 'Enclosure',
197+
'featureValues' => [['value' => 'SOT-23']]
198+
]
199+
]
200+
]
201+
],
202+
'price' => ['value' => 1, 'currencyIso' => 'EUR']
203+
];
204+
205+
$dto = $method->invoke($this->provider, $product);
206+
$this->assertSame('SOT-23', $dto->footprint);
207+
}
208+
209+
// ---------------------------------------------------------
210+
// Test: price formatting
211+
// ---------------------------------------------------------
212+
public function testPriceFormatting(): void
213+
{
214+
$detailPrice = [
215+
[
216+
'minQuantity' => 1,
217+
'value' => 0.0885,
218+
'currencyIso' => 'EUR'
219+
]
220+
];
221+
222+
$method = new \ReflectionMethod(BuerklinProvider::class, 'pricesToVendorInfo');
223+
$method->setAccessible(true);
224+
225+
$vendorInfo = $method->invoke($this->provider, 'SKU1', 'https://x', $detailPrice);
226+
227+
$price = $vendorInfo[0]->prices[0];
228+
229+
$this->assertSame('0.0885', $price->price);
230+
}
231+
232+
// ---------------------------------------------------------
233+
// Test: batch search produces SearchResultDTO[]
234+
// ---------------------------------------------------------
235+
public function testBatchSearchReturnsSearchResultDTO(): void
236+
{
237+
// Mock searchByKeyword to avoid HTTP
238+
$mockDetail = new PartDetailDTO(
239+
provider_key: 'buerklin',
240+
provider_id: 'TESTID',
241+
name: 'Zener',
242+
description: 'Desc'
243+
);
244+
245+
$provider = $this->getMockBuilder(BuerklinProvider::class)
246+
->setConstructorArgs([
247+
$this->httpClient,
248+
$this->tokenManager,
249+
$this->cache,
250+
$this->settings
251+
])
252+
->onlyMethods(['searchByKeyword'])
253+
->getMock();
254+
255+
$provider->method('searchByKeyword')->willReturn([$mockDetail]);
256+
257+
$result = $provider->searchByKeywordsBatch(['ABC']);
258+
259+
$this->assertArrayHasKey('ABC', $result);
260+
$this->assertInstanceOf(SearchResultDTO::class, $result['ABC'][0]);
261+
$this->assertSame('Zener', $result['ABC'][0]->name);
262+
}
263+
264+
// ---------------------------------------------------------
265+
// Test: convertPartDetailToSearchResult
266+
// ---------------------------------------------------------
267+
public function testConvertPartDetailToSearchResult(): void
268+
{
269+
$detail = new PartDetailDTO(
270+
provider_key: 'buerklin',
271+
provider_id: 'X1',
272+
name: 'PartX',
273+
description: 'D',
274+
preview_image_url: 'https://img'
275+
);
276+
277+
$method = new \ReflectionMethod(BuerklinProvider::class, 'convertPartDetailToSearchResult');
278+
$method->setAccessible(true);
279+
280+
$dto = $method->invoke($this->provider, $detail);
281+
282+
$this->assertInstanceOf(SearchResultDTO::class, $dto);
283+
$this->assertSame('X1', $dto->provider_id);
284+
$this->assertSame('PartX', $dto->name);
285+
$this->assertSame('https://img', $dto->preview_image_url);
286+
}
287+
}

0 commit comments

Comments
 (0)