mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-01-13 21:59:34 +00:00
* Fixed Typos and mistranslations in GDPR mode (DSGVO Modus) Fixed Typo enviroment * Create BuerklinProvider based on LCSCProvider * Update GET URLs for Buerklin * Add getToken function analog to Octopart * Remove line break in docs * Remove trailing / in ENDPOINT_URL Use Autowire to use values of environment variables Remove unwanted Code from LCSC-Provider Map json response to DTO variables * Fix variable reference errors ($term → $keyword) Ensure array keys exist before accessing them Optimize API calls to prevent unnecessary requests Improve error handling for better debugging Enhance readability and maintainability of functions * Bumped version v1.16.2 * Update BuerklinProvider.php Change Order of Capabilities * Change order of capabilities in LCSCProvider.php * Change order of capabilities in PollinProvider.php * Try to fix getToken BuerklinProvider.php * Add ip_buerklin_oauth to knpu_oauth2_client.yaml * Update buerklin authorize URL in knpu_oauth2_client.yaml * Update knpu_oauth2_client.yaml * Adapt Buerklin InfoProvider to new Settings mechanism * According to Buerklin API spec it's really 'token' as urlAuthorize endpoint * Rückgabewert ist schon ein Array deshalb kein toArray * Fix API-Access, add image, price and parameter retrieval (Datasheets not yet implemented because it is not available in the API response) * Add Caching of requests, use default query params (language and currency) using a function, Fix Footprint assignment, translate German code comments * Remove DATASHEET from ProviderCapabilities because the Bürklin API doesn't include Datasheet URLs at the moment, more reverse engineering needed * Update BuerklinSettings with existing translatable strings * Improve Buerklin Settings Page * Added Translation strings for Buerklin Info Provider * Improve Buerklin Provider help message * Adapt Buerklin-provider to new settings system * Adapt Buerklin-provider to new settings system: add missing instance of BuerklinSettings * Improve Compliance Parameters parsing * Remove language-dependent RoHs Date code and use shortened ISO format, Add Customs Code without parseValueField * Fix no results for keyword search * Implement BatchInfoProviderInterface for Buerklin Provider * Rename searchBatch to searchByKeywordsBatch to correctly implement BatchInfoProviderInterface * Fix Bulk Info Provider Import for Buerklin * Tranlate comments to English to prepare for Pull-Request * Add phpUnit unit tests for BuerklinProvider * Try fixing PHPStan issues * Remove OAuthTokenManager from BuerklinProviderTest Removed OAuthTokenManager mock from BuerklinProviderTest setup. * Fix Settings must not be instantiated directly * Fix UnitTest for value_typ *edd5fb3319 (r2622249199)Revert "Change order of capabilities in LCSCProvider.php" This reverts commitdfd6f33e52. *edd5fb3319 (r2622249861)Revert "Change order of capabilities in PollinProvider.php" This reverts commitfc2e7265be. * Use language setting for ProductShortURL * Update default language for Buerklin provider to English in documentation * Add suggested improvements from SonarQube * Removed unused use directives * Revert SonarQube proposed change. Having more than one return is acceptable nowadays * Improve getProviderInfo: disable oauth_app_name, add settings_class, improve disabled_help * Implement retrieveROPCToken as proposed in https://github.com/Part-DB/Part-DB-server/pull/1151#discussion_r2622976206 * Add missing ) to retrieveROPCToken * add use OAuthTokenManager and create instance in constructor * Revert the following commits that tried to implement getToken using OAuthTokenManager Revert "add use OAuthTokenManager and create instance in constructor"This reverts commit 2a1e7c9b0974ebd7e082d5a2fa62753d6254a767.Revert "Add missing ) to retrieveROPCToken"This reverts commit8df5cfc49e. Revert "Implement retrieveROPCToken as proposed in https://github.com/Part-DB/Part-DB-server/pull/1151#discussion_r2622976206" This reverts commit66cc732082. * Remove OAuthTokenManager leftovers * Disable buerklin provider if settings fields are empty * Improved docs * Added TODO comment --------- Co-authored-by: root <root@part-db.fritz.box> Co-authored-by: Jan Böhmer <mail@jan-boehmer.de>
271 lines
9 KiB
PHP
271 lines
9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Services\InfoProviderSystem\Providers;
|
|
|
|
use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
|
|
use App\Services\InfoProviderSystem\DTOs\SearchResultDTO;
|
|
use App\Services\InfoProviderSystem\Providers\BuerklinProvider;
|
|
use App\Settings\InfoProviderSystem\BuerklinSettings;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Psr\Cache\CacheItemInterface;
|
|
use Psr\Cache\CacheItemPoolInterface;
|
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
|
use Symfony\Contracts\HttpClient\ResponseInterface;
|
|
|
|
/**
|
|
* Full behavioral test suite for BuerklinProvider.
|
|
* Includes parameter parsing, compliance parsing, images, prices and batch mode.
|
|
*/
|
|
class BuerklinProviderTest extends TestCase
|
|
{
|
|
private HttpClientInterface $httpClient;
|
|
private CacheItemPoolInterface $cache;
|
|
private BuerklinSettings $settings;
|
|
private BuerklinProvider $provider;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->httpClient = $this->createMock(HttpClientInterface::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);
|
|
|
|
// IMPORTANT: Settings must not be instantiated directly (SettingsBundle forbids constructor)
|
|
$ref = new \ReflectionClass(BuerklinSettings::class);
|
|
/** @var BuerklinSettings $settings */
|
|
$settings = $ref->newInstanceWithoutConstructor();
|
|
|
|
$settings->clientId = 'CID';
|
|
$settings->secret = 'SECRET';
|
|
$settings->username = 'USER';
|
|
$settings->password = 'PASS';
|
|
$settings->language = 'en';
|
|
$settings->currency = 'EUR';
|
|
|
|
$this->settings = $settings;
|
|
|
|
$this->provider = new BuerklinProvider(
|
|
client: $this->httpClient,
|
|
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((string) $url, $expectedUrl)),
|
|
$this->anything()
|
|
)
|
|
->willReturn($response);
|
|
}
|
|
|
|
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->assertNull($params[0]->value_text);
|
|
$this->assertSame(12.0, $params[0]->value_typ);
|
|
$this->assertNull($params[0]->value_min);
|
|
$this->assertNull($params[0]->value_max);
|
|
$this->assertSame('V', $params[0]->unit);
|
|
|
|
$this->assertSame('Length', $params[1]->name);
|
|
$this->assertNull($params[1]->value_text);
|
|
$this->assertSame(2.9, $params[1]->value_typ);
|
|
$this->assertSame('mm', $params[1]->unit);
|
|
|
|
$this->assertSame('Assembly', $params[2]->name);
|
|
$this->assertSame('SMD', $params[2]->value_text);
|
|
$this->assertNull($params[2]->unit);
|
|
}
|
|
|
|
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']);
|
|
}
|
|
|
|
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'], // duplicate
|
|
['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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
public function testBatchSearchReturnsSearchResultDTO(): void
|
|
{
|
|
$mockDetail = new PartDetailDTO(
|
|
provider_key: 'buerklin',
|
|
provider_id: 'TESTID',
|
|
name: 'Zener',
|
|
description: 'Desc'
|
|
);
|
|
|
|
$provider = $this->getMockBuilder(BuerklinProvider::class)
|
|
->setConstructorArgs([
|
|
$this->httpClient,
|
|
$this->cache,
|
|
$this->settings
|
|
])
|
|
->onlyMethods(['searchByKeyword'])
|
|
->getMock();
|
|
|
|
$provider->method('searchByKeyword')->willReturn([$mockDetail]);
|
|
|
|
$result = $provider->searchByKeywordsBatch(['ABC']);
|
|
|
|
$this->assertArrayHasKey('ABC', $result);
|
|
$this->assertIsArray($result['ABC']);
|
|
$this->assertCount(1, $result['ABC']);
|
|
$this->assertInstanceOf(SearchResultDTO::class, $result['ABC'][0]);
|
|
$this->assertSame('Zener', $result['ABC'][0]->name);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|