From c60fafb66ad9d2a75d6524fa21ead4b33b9edf89 Mon Sep 17 00:00:00 2001 From: Marc Kreidler Date: Thu, 11 Dec 2025 21:54:23 +0100 Subject: [PATCH] Add phpUnit unit tests for BuerklinProvider --- .../Providers/BuerklinProviderTest.php | 287 ++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 tests/Services/InfoProviderSystem/Providers/BuerklinProviderTest.php diff --git a/tests/Services/InfoProviderSystem/Providers/BuerklinProviderTest.php b/tests/Services/InfoProviderSystem/Providers/BuerklinProviderTest.php new file mode 100644 index 00000000..1314a4dc --- /dev/null +++ b/tests/Services/InfoProviderSystem/Providers/BuerklinProviderTest.php @@ -0,0 +1,287 @@ +httpClient = $this->createMock(HttpClientInterface::class); + $this->tokenManager = $this->createMock(OAuthTokenManager::class); + + // Cache mock + $cacheItem = $this->createMock(CacheItemInterface::class); + $cacheItem->method('isHit')->willReturn(false); + $cacheItem->method('set')->willReturn($cacheItem); + + $this->cache = $this->createMock(CacheItemPoolInterface::class); + $this->cache->method('getItem')->willReturn($cacheItem); + + $this->settings = new BuerklinSettings(); + $this->settings->clientId = 'CID'; + $this->settings->secret = 'SECRET'; + $this->settings->username = 'USER'; + $this->settings->password = 'PASS'; + $this->settings->language = 'en'; + $this->settings->currency = 'EUR'; + + $this->provider = new BuerklinProvider( + client: $this->httpClient, + authTokenManager: $this->tokenManager, + partInfoCache: $this->cache, + settings: $this->settings, + ); + } + + private function mockApi(string $expectedUrl, array $jsonResponse): void + { + $response = $this->createMock(ResponseInterface::class); + $response->method('toArray')->willReturn($jsonResponse); + + $this->httpClient + ->method('request') + ->with( + 'GET', + $this->callback(fn($url) => str_contains($url, $expectedUrl)), + $this->anything() + ) + ->willReturn($response); + } + + // --------------------------------------------------------- + // Test: attributesToParameters + // --------------------------------------------------------- + public function testAttributesToParametersParsesUnitsAndValues(): void + { + $method = new \ReflectionMethod(BuerklinProvider::class, 'attributesToParameters'); + $method->setAccessible(true); + + $features = [ + [ + 'name' => 'Zener voltage', + 'featureUnit' => ['symbol' => 'V'], + 'featureValues' => [ + ['value' => '12'] + ] + ], + [ + 'name' => 'Length', + 'featureUnit' => ['symbol' => 'mm'], + 'featureValues' => [ + ['value' => '2.9'] + ] + ], + [ + 'name' => 'Assembly', + 'featureUnit' => [], + 'featureValues' => [ + ['value' => 'SMD'] + ] + ] + ]; + + $params = $method->invoke($this->provider, $features, ''); + + $this->assertCount(3, $params); + + $this->assertSame('Zener voltage', $params[0]->name); + $this->assertSame('12', $params[0]->value_text); + $this->assertSame('V', $params[0]->unit); + + $this->assertSame('Length', $params[1]->name); + $this->assertSame('2.9', $params[1]->value_text); + $this->assertSame('mm', $params[1]->unit); + + $this->assertSame('Assembly', $params[2]->name); + $this->assertSame('SMD', $params[2]->value_text); + $this->assertNull($params[2]->unit); + } + + // --------------------------------------------------------- + // Test: complianceToParameters + // --------------------------------------------------------- + public function testComplianceParameters(): void + { + $method = new \ReflectionMethod(BuerklinProvider::class, 'complianceToParameters'); + $method->setAccessible(true); + + $product = [ + 'labelRoHS' => 'Yes', + 'dateRoHS' => '2015-03-31T00:00+0000', + 'SVHC' => true, + 'hazardousGood' => false, + 'hazardousMaterials' => false, + 'countryOfOrigin' => 'China', + 'articleCustomsCode' => '85411000' + ]; + + $params = $method->invoke($this->provider, $product, 'Compliance'); + + $map = []; + foreach ($params as $p) { + $map[$p->name] = $p->value_text; + } + + $this->assertSame('Yes', $map['RoHS conform']); + $this->assertSame('2015-03-31', $map['RoHS date']); + $this->assertSame('Yes', $map['SVHC free']); + $this->assertSame('No', $map['Hazardous good']); + $this->assertSame('No', $map['Hazardous materials']); + $this->assertSame('China', $map['Country of origin']); + $this->assertSame('85411000', $map['Customs code']); + } + + // --------------------------------------------------------- + // Test: image handling + // --------------------------------------------------------- + public function testImageSelectionPrefersZoomAndDeduplicates(): void + { + $method = new \ReflectionMethod(BuerklinProvider::class, 'getProductImages'); + $method->setAccessible(true); + + $images = [ + ['format' => 'product', 'url' => '/img/a.webp'], + ['format' => 'zoom', 'url' => '/img/z.webp'], + ['format' => 'zoom', 'url' => '/img/z.webp'], // dup + ['format' => 'thumbnail', 'url' => '/img/t.webp'] + ]; + + $results = $method->invoke($this->provider, $images); + + $this->assertCount(1, $results); + $this->assertSame('https://www.buerklin.com/img/z.webp', $results[0]->url); + } + + // --------------------------------------------------------- + // Test: footprint extraction + // --------------------------------------------------------- + public function testFootprintExtraction(): void + { + $method = new \ReflectionMethod(BuerklinProvider::class, 'getPartDetail'); + $method->setAccessible(true); + + $product = [ + 'code' => 'TEST1', + 'manufacturerProductId' => 'ABC', + 'description' => 'X', + 'images' => [], + 'classifications' => [ + [ + 'name' => 'Cat', + 'features' => [ + [ + 'name' => 'Enclosure', + 'featureValues' => [['value' => 'SOT-23']] + ] + ] + ] + ], + 'price' => ['value' => 1, 'currencyIso' => 'EUR'] + ]; + + $dto = $method->invoke($this->provider, $product); + $this->assertSame('SOT-23', $dto->footprint); + } + + // --------------------------------------------------------- + // Test: price formatting + // --------------------------------------------------------- + public function testPriceFormatting(): void + { + $detailPrice = [ + [ + 'minQuantity' => 1, + 'value' => 0.0885, + 'currencyIso' => 'EUR' + ] + ]; + + $method = new \ReflectionMethod(BuerklinProvider::class, 'pricesToVendorInfo'); + $method->setAccessible(true); + + $vendorInfo = $method->invoke($this->provider, 'SKU1', 'https://x', $detailPrice); + + $price = $vendorInfo[0]->prices[0]; + + $this->assertSame('0.0885', $price->price); + } + + // --------------------------------------------------------- + // Test: batch search produces SearchResultDTO[] + // --------------------------------------------------------- + public function testBatchSearchReturnsSearchResultDTO(): void + { + // Mock searchByKeyword to avoid HTTP + $mockDetail = new PartDetailDTO( + provider_key: 'buerklin', + provider_id: 'TESTID', + name: 'Zener', + description: 'Desc' + ); + + $provider = $this->getMockBuilder(BuerklinProvider::class) + ->setConstructorArgs([ + $this->httpClient, + $this->tokenManager, + $this->cache, + $this->settings + ]) + ->onlyMethods(['searchByKeyword']) + ->getMock(); + + $provider->method('searchByKeyword')->willReturn([$mockDetail]); + + $result = $provider->searchByKeywordsBatch(['ABC']); + + $this->assertArrayHasKey('ABC', $result); + $this->assertInstanceOf(SearchResultDTO::class, $result['ABC'][0]); + $this->assertSame('Zener', $result['ABC'][0]->name); + } + + // --------------------------------------------------------- + // Test: convertPartDetailToSearchResult + // --------------------------------------------------------- + public function testConvertPartDetailToSearchResult(): void + { + $detail = new PartDetailDTO( + provider_key: 'buerklin', + provider_id: 'X1', + name: 'PartX', + description: 'D', + preview_image_url: 'https://img' + ); + + $method = new \ReflectionMethod(BuerklinProvider::class, 'convertPartDetailToSearchResult'); + $method->setAccessible(true); + + $dto = $method->invoke($this->provider, $detail); + + $this->assertInstanceOf(SearchResultDTO::class, $dto); + $this->assertSame('X1', $dto->provider_id); + $this->assertSame('PartX', $dto->name); + $this->assertSame('https://img', $dto->preview_image_url); + } +}