mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-08-02 22:51:48 +00:00
Fix TME provider null-safety and image URL bugs, expand test coverage
Add missing null-safety for category field and fix a typo that discarded high_resolution image variants. Port additional test coverage (nullable fields, document type filtering, footprint preference, status mapping) from carmisergio's V2 API test suite, adapted to match this repo's current TMEProvider/TMEClient implementation. Co-authored-by: Sergio Carmine <me@sergiocarmi.net> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
d673e60d6a
commit
523c3f57fa
2 changed files with 201 additions and 75 deletions
|
|
@ -104,7 +104,7 @@ class TMEProvider implements InfoProviderInterface, URLHandlerInfoProviderInterf
|
||||||
provider_id: $product['symbol'],
|
provider_id: $product['symbol'],
|
||||||
name: $product['manufacturer_symbols'][0] ?? $product['symbol'],
|
name: $product['manufacturer_symbols'][0] ?? $product['symbol'],
|
||||||
description: $product['description'],
|
description: $product['description'],
|
||||||
category: $product['category']['name'],
|
category: $product['category']['name'] ?? null,
|
||||||
manufacturer: $product['manufacturer']['name'] ?? null,
|
manufacturer: $product['manufacturer']['name'] ?? null,
|
||||||
mpn: $product['manufacturer_symbols'][0] ?? null,
|
mpn: $product['manufacturer_symbols'][0] ?? null,
|
||||||
preview_image_url: $this->normalizeURL($product['assets']['primary_photo']['prime'] ?? null),
|
preview_image_url: $this->normalizeURL($product['assets']['primary_photo']['prime'] ?? null),
|
||||||
|
|
@ -139,7 +139,7 @@ class TMEProvider implements InfoProviderInterface, URLHandlerInfoProviderInterf
|
||||||
provider_id: $product['symbol'],
|
provider_id: $product['symbol'],
|
||||||
name: $product['manufacturer_symbols'][0] ?? $product['symbol'],
|
name: $product['manufacturer_symbols'][0] ?? $product['symbol'],
|
||||||
description: $product['description'],
|
description: $product['description'],
|
||||||
category: $product['category']['name'],
|
category: $product['category']['name'] ?? null,
|
||||||
manufacturer: $product['manufacturer']['name'] ?? null,
|
manufacturer: $product['manufacturer']['name'] ?? null,
|
||||||
mpn: $product['manufacturer_symbols'][0] ?? null,
|
mpn: $product['manufacturer_symbols'][0] ?? null,
|
||||||
preview_image_url: $this->normalizeURL($product['assets']['primary_photo']['prime'] ?? null),
|
preview_image_url: $this->normalizeURL($product['assets']['primary_photo']['prime'] ?? null),
|
||||||
|
|
@ -183,7 +183,7 @@ class TMEProvider implements InfoProviderInterface, URLHandlerInfoProviderInterf
|
||||||
$images = [];
|
$images = [];
|
||||||
foreach ($element['assets']['additional']['elements'] ?? [] as $photo) {
|
foreach ($element['assets']['additional']['elements'] ?? [] as $photo) {
|
||||||
$images[] = new FileDTO(
|
$images[] = new FileDTO(
|
||||||
url: $this->normalizeURL($image['high_resolution'] ?? $photo['prime']),
|
url: $this->normalizeURL($photo['high_resolution'] ?? $photo['prime']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||||
namespace App\Tests\Services\InfoProviderSystem\Providers;
|
namespace App\Tests\Services\InfoProviderSystem\Providers;
|
||||||
|
|
||||||
use App\Entity\Parts\ManufacturingStatus;
|
use App\Entity\Parts\ManufacturingStatus;
|
||||||
|
use App\Services\InfoProviderSystem\DTOs\FileDTO;
|
||||||
use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
|
use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
|
||||||
use App\Services\InfoProviderSystem\DTOs\ProviderInfoDTO;
|
use App\Services\InfoProviderSystem\DTOs\ProviderInfoDTO;
|
||||||
use App\Services\InfoProviderSystem\DTOs\PurchaseInfoDTO;
|
use App\Services\InfoProviderSystem\DTOs\PurchaseInfoDTO;
|
||||||
|
|
@ -55,6 +56,11 @@ final class TMEProviderTest extends TestCase
|
||||||
$this->provider = new TMEProvider(new TMEClient($this->httpClient, $this->settings, new ArrayAdapter()), $this->settings, $this->httpClient);
|
$this->provider = new TMEProvider(new TMEClient($this->httpClient, $this->settings, new ArrayAdapter()), $this->settings, $this->httpClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function newProvider(): TMEProvider
|
||||||
|
{
|
||||||
|
return new TMEProvider(new TMEClient($this->httpClient, $this->settings, new ArrayAdapter()), $this->settings, $this->httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
// --- Mock response helpers ---
|
// --- Mock response helpers ---
|
||||||
|
|
||||||
/** OAuth2 token response – always the first reply in a sequence that triggers an API call */
|
/** OAuth2 token response – always the first reply in a sequence that triggers an API call */
|
||||||
|
|
@ -88,31 +94,27 @@ final class TMEProviderTest extends TestCase
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function mockFilesList(array $products): MockResponse
|
private function mockFilesList(array $elements): MockResponse
|
||||||
{
|
{
|
||||||
return new MockResponse(json_encode([
|
return new MockResponse(json_encode([
|
||||||
'status' => 'OK',
|
'status' => 'OK',
|
||||||
'data' => ['elements' => $products],
|
'data' => ['elements' => $elements],
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function mockParametersList(array $products): MockResponse
|
private function mockParametersList(array $elements): MockResponse
|
||||||
{
|
{
|
||||||
return new MockResponse(json_encode([
|
return new MockResponse(json_encode([
|
||||||
'status' => 'OK',
|
'status' => 'OK',
|
||||||
'data' => ['elements' => $products],
|
'data' => ['elements' => $elements],
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function mockPricesData(string $currency, string $priceType, array $products): MockResponse
|
private function mockPrices(string $currency, string $priceType, array $elements): MockResponse
|
||||||
{
|
{
|
||||||
return new MockResponse(json_encode([
|
return new MockResponse(json_encode([
|
||||||
'status' => 'OK',
|
'status' => 'OK',
|
||||||
'data' => [
|
'data' => ['elements' => $elements],
|
||||||
'currency' => $currency,
|
|
||||||
'price_type' => $priceType,
|
|
||||||
'elements' => $products,
|
|
||||||
],
|
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,16 +123,17 @@ final class TMEProviderTest extends TestCase
|
||||||
private function smd0603Product(): array
|
private function smd0603Product(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'symbol' => 'SMD0603-5K1-1%',
|
'symbol' => 'SMD0603-5K1-1%',
|
||||||
'original_symbol' => '0603SAF5101T5E',
|
'ean' => '978020137962',
|
||||||
'producer' => 'ROYALOHM',
|
'category' => ['name' => 'SMD resistors'],
|
||||||
'description' => 'Resistor: thick film; SMD; 0603; 5.1kΩ; 0.1W; ±1%; 50V; -55÷155°C',
|
'manufacturer_symbols' => ['0603SAF5101T5E', 'another_symbol'],
|
||||||
'category' => 'SMD resistors',
|
'manufacturer' => ['name' => 'ROYALOHM'],
|
||||||
'photo' => '//ce8dc832c.cloudimg.io/v7/_cdn_/E9/C2/B0/00/0/732318_1.jpg',
|
'description' => 'Resistor: thick film; SMD; 0603; 5.1kΩ; 0.1W; ±1%; 50V; -55÷155°C',
|
||||||
'statuses' => [],
|
'assets' => [
|
||||||
'product_information_page' => '//www.tme.eu/en/details/smd0603-5k1-1%/smd-resistors/royalohm/0603saf5101t5e/',
|
'primary_photo' => ['prime' => '//ce8dc832c.cloudimg.io/v7/_cdn_/E9/C2/B0/00/0/732318_1.jpg'],
|
||||||
'weight' => 0.021,
|
],
|
||||||
'weight_unit' => 'g',
|
'weight' => ['value' => 0.021, 'unit' => 'g'],
|
||||||
|
'product_status' => [],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,10 +151,16 @@ final class TMEProviderTest extends TestCase
|
||||||
{
|
{
|
||||||
return $this->mockFilesList([[
|
return $this->mockFilesList([[
|
||||||
'symbol' => 'SMD0603-5K1-1%',
|
'symbol' => 'SMD0603-5K1-1%',
|
||||||
'photos' => [],
|
|
||||||
'documents' => [
|
'documents' => [
|
||||||
['url' => '//www.tme.eu/Document/b315665a56acbc42df513c99b390ad98/ROYALOHM-THICKFILM.pdf'],
|
'elements' => [
|
||||||
['url' => '//www.tme.eu/Document/c283990e907c122bb808207d1578ac7f/POWER_RATING-DTE.pdf'],
|
['url' => '//www.tme.eu/Document/b315665a56acbc42df513c99b390ad98/ROYALOHM-THICKFILM.pdf', 'type' => 'DTE', 'file_name' => 'ROYALOHM-THICKFILM.pdf'],
|
||||||
|
['url' => '//www.tme.eu/Document/c283990e907c122bb808207d1578ac7f/POWER_RATING-DTE.pdf', 'type' => 'DTE', 'file_name' => 'POWER_RATING-DTE.pdf'],
|
||||||
|
// Firmware document, must be filtered out as it is not a valid document type
|
||||||
|
['url' => '//www.tme.eu/Document/some_firmware.bin', 'type' => 'SFT', 'file_name' => 'firmware.bin'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'assets' => [
|
||||||
|
'additional' => ['elements' => []],
|
||||||
],
|
],
|
||||||
]]);
|
]]);
|
||||||
}
|
}
|
||||||
|
|
@ -161,23 +170,29 @@ final class TMEProviderTest extends TestCase
|
||||||
return $this->mockParametersList([[
|
return $this->mockParametersList([[
|
||||||
'symbol' => 'SMD0603-5K1-1%',
|
'symbol' => 'SMD0603-5K1-1%',
|
||||||
'parameters' => [
|
'parameters' => [
|
||||||
['id' => 34, 'name' => 'Type of resistor', 'value' => 'thick film'],
|
'elements' => [
|
||||||
['id' => 35, 'name' => 'Case - mm', 'value' => '1608'],
|
['id' => 34, 'name' => 'Type of resistor', 'values' => [['value' => 'thick film']]],
|
||||||
['id' => 38, 'name' => 'Resistance', 'value' => '5.1kΩ'],
|
['id' => 35, 'name' => 'Case - mm', 'values' => [['value' => '1608']]],
|
||||||
['id' => 39, 'name' => 'Tolerance', 'value' => '±1%'],
|
['id' => 38, 'name' => 'Resistance', 'values' => [['value' => '5.1kΩ']]],
|
||||||
['id' => 120, 'name' => 'Operating voltage', 'value' => '50V'],
|
['id' => 39, 'name' => 'Tolerance', 'values' => [['value' => '±1%']]],
|
||||||
|
['id' => 120, 'name' => 'Operating voltage', 'values' => [['value' => '50V']]],
|
||||||
|
],
|
||||||
],
|
],
|
||||||
]]);
|
]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function smd0603Prices(): MockResponse
|
private function smd0603Prices(): MockResponse
|
||||||
{
|
{
|
||||||
return $this->mockPricesData('EUR', 'NET', [[
|
return $this->mockPrices('EUR', 'NET', [[
|
||||||
'symbol' => 'SMD0603-5K1-1%',
|
'symbol' => 'SMD0603-5K1-1%',
|
||||||
'price_list' => [
|
'prices' => [
|
||||||
['amount' => 100, 'price_value' => 0.01077],
|
'currency' => 'EUR',
|
||||||
['amount' => 1000, 'price_value' => 0.00291],
|
'type' => 'NET',
|
||||||
['amount' => 5000, 'price_value' => 0.00150],
|
'elements' => [
|
||||||
|
['amount' => 100, 'price' => 0.01077],
|
||||||
|
['amount' => 1000, 'price' => 0.00291],
|
||||||
|
['amount' => 5000, 'price' => 0.00150],
|
||||||
|
],
|
||||||
],
|
],
|
||||||
]]);
|
]]);
|
||||||
}
|
}
|
||||||
|
|
@ -185,16 +200,16 @@ final class TMEProviderTest extends TestCase
|
||||||
private function etqp3mProduct(): array
|
private function etqp3mProduct(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'symbol' => 'ETQP3M6R8KVP',
|
'symbol' => 'ETQP3M6R8KVP',
|
||||||
'original_symbol' => 'ETQP3M6R8KVP',
|
'category' => ['name' => 'Inductors'],
|
||||||
'producer' => 'PANASONIC',
|
'manufacturer_symbols' => ['ETQP3M6R8KVP'],
|
||||||
'description' => 'Inductor: wire; SMD; 6.8uH; 2.9A; R: 65.7mΩ; ±20%; ETQP3M; 5.5x5x3mm',
|
'manufacturer' => ['name' => 'PANASONIC'],
|
||||||
'category' => 'Inductors',
|
'description' => 'Inductor: wire; SMD; 6.8uH; 2.9A; R: 65.7mΩ; ±20%; ETQP3M; 5.5x5x3mm',
|
||||||
'photo' => '//ce8dc832c.cloudimg.io/v7/_cdn_/9E/27/A0/00/0/684777_1.jpg',
|
'assets' => [
|
||||||
'statuses' => [],
|
'primary_photo' => ['prime' => '//ce8dc832c.cloudimg.io/v7/_cdn_/9E/27/A0/00/0/684777_1.jpg'],
|
||||||
'product_information_page' => '//www.tme.eu/en/details/etqp3m6r8kvp/inductors/panasonic/',
|
],
|
||||||
'weight' => 0.44,
|
'weight' => ['value' => 0.44, 'unit' => 'g'],
|
||||||
'weight_unit' => 'g',
|
'product_status' => [],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,10 +222,21 @@ final class TMEProviderTest extends TestCase
|
||||||
{
|
{
|
||||||
return $this->mockFilesList([[
|
return $this->mockFilesList([[
|
||||||
'symbol' => 'ETQP3M6R8KVP',
|
'symbol' => 'ETQP3M6R8KVP',
|
||||||
'photos' => [],
|
|
||||||
'documents' => [
|
'documents' => [
|
||||||
['url' => '//www.tme.eu/Document/50a845881f09d8a2248350946e11df38/AGL0000C63.pdf'],
|
'elements' => [
|
||||||
['url' => '//www.tme.eu/Document/8480690a42fa577214e35e33d3fc8d77/ETQP3M100KVN-LNK.txt'],
|
['url' => '//www.tme.eu/Document/50a845881f09d8a2248350946e11df38/AGL0000C63.pdf', 'type' => 'DTE', 'file_name' => 'AGL0000C63.pdf'],
|
||||||
|
['url' => '//www.tme.eu/Document/8480690a42fa577214e35e33d3fc8d77/ETQP3M100KVN-LNK.txt', 'type' => 'KCH', 'file_name' => 'ETQP3M100KVN-LNK.txt'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'assets' => [
|
||||||
|
'additional' => [
|
||||||
|
'elements' => [
|
||||||
|
// Only a low-res "prime" image available -> that one must be used
|
||||||
|
['prime' => '//ce8dc832c.cloudimg.io/v7/_cdn_/additional1_prime.jpg'],
|
||||||
|
// Both available -> the high-resolution one must be preferred
|
||||||
|
['prime' => '//ce8dc832c.cloudimg.io/v7/_cdn_/additional2_prime.jpg', 'high_resolution' => '//ce8dc832c.cloudimg.io/v7/_cdn_/additional2_high_res.jpg'],
|
||||||
|
],
|
||||||
|
],
|
||||||
],
|
],
|
||||||
]]);
|
]]);
|
||||||
}
|
}
|
||||||
|
|
@ -220,21 +246,27 @@ final class TMEProviderTest extends TestCase
|
||||||
return $this->mockParametersList([[
|
return $this->mockParametersList([[
|
||||||
'symbol' => 'ETQP3M6R8KVP',
|
'symbol' => 'ETQP3M6R8KVP',
|
||||||
'parameters' => [
|
'parameters' => [
|
||||||
['id' => 566, 'name' => 'Inductance', 'value' => '6.8µH'],
|
'elements' => [
|
||||||
['id' => 370, 'name' => 'Operating current', 'value' => '2.9A'],
|
['id' => 566, 'name' => 'Inductance', 'values' => [['value' => '6.8µH']]],
|
||||||
['id' => 39, 'name' => 'Tolerance', 'value' => '±20%'],
|
['id' => 370, 'name' => 'Operating current', 'values' => [['value' => '2.9A']]],
|
||||||
|
['id' => 39, 'name' => 'Tolerance', 'values' => [['value' => '±20%']]],
|
||||||
|
],
|
||||||
],
|
],
|
||||||
]]);
|
]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function etqp3mPrices(): MockResponse
|
private function etqp3mPrices(): MockResponse
|
||||||
{
|
{
|
||||||
return $this->mockPricesData('EUR', 'NET', [[
|
return $this->mockPrices('EUR', 'NET', [[
|
||||||
'symbol' => 'ETQP3M6R8KVP',
|
'symbol' => 'ETQP3M6R8KVP',
|
||||||
'price_list' => [
|
'prices' => [
|
||||||
['amount' => 1, 'price_value' => 0.589],
|
'currency' => 'EUR',
|
||||||
['amount' => 5, 'price_value' => 0.429],
|
'type' => 'NET',
|
||||||
['amount' => 10, 'price_value' => 0.399],
|
'elements' => [
|
||||||
|
['amount' => 1, 'price' => 0.589],
|
||||||
|
['amount' => 5, 'price' => 0.429],
|
||||||
|
['amount' => 10, 'price' => 0.399],
|
||||||
|
],
|
||||||
],
|
],
|
||||||
]]);
|
]]);
|
||||||
}
|
}
|
||||||
|
|
@ -259,7 +291,7 @@ final class TMEProviderTest extends TestCase
|
||||||
public function testIsActiveWithoutCredentials(): void
|
public function testIsActiveWithoutCredentials(): void
|
||||||
{
|
{
|
||||||
$this->settings->apiToken = null;
|
$this->settings->apiToken = null;
|
||||||
$provider = new TMEProvider(new TMEClient($this->httpClient, $this->settings, new ArrayAdapter()), $this->settings, $this->httpClient);
|
$provider = $this->newProvider();
|
||||||
$this->assertFalse($provider->isActive());
|
$this->assertFalse($provider->isActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -353,15 +385,45 @@ final class TMEProviderTest extends TestCase
|
||||||
$this->assertIsArray($results);
|
$this->assertIsArray($results);
|
||||||
$this->assertCount(1, $results);
|
$this->assertCount(1, $results);
|
||||||
$this->assertInstanceOf(SearchResultDTO::class, $results[0]);
|
$this->assertInstanceOf(SearchResultDTO::class, $results[0]);
|
||||||
|
$this->assertSame('tme', $results[0]->provider_key);
|
||||||
$this->assertSame('SMD0603-5K1-1%', $results[0]->provider_id);
|
$this->assertSame('SMD0603-5K1-1%', $results[0]->provider_id);
|
||||||
$this->assertSame('0603SAF5101T5E', $results[0]->name);
|
$this->assertSame('0603SAF5101T5E', $results[0]->name);
|
||||||
$this->assertSame('ROYALOHM', $results[0]->manufacturer);
|
$this->assertSame('ROYALOHM', $results[0]->manufacturer);
|
||||||
$this->assertSame('SMD resistors', $results[0]->category);
|
$this->assertSame('SMD resistors', $results[0]->category);
|
||||||
|
$this->assertSame('0603SAF5101T5E', $results[0]->mpn);
|
||||||
|
$this->assertSame('978020137962', $results[0]->gtin);
|
||||||
|
$this->assertSame('https://ce8dc832c.cloudimg.io/v7/_cdn_/E9/C2/B0/00/0/732318_1.jpg', $results[0]->preview_image_url);
|
||||||
$this->assertSame(ManufacturingStatus::ACTIVE, $results[0]->manufacturing_status);
|
$this->assertSame(ManufacturingStatus::ACTIVE, $results[0]->manufacturing_status);
|
||||||
$this->assertSame(
|
$this->assertSame('https://www.tme.eu/de/en/details/SMD0603-5K1-1%/', $results[0]->provider_url);
|
||||||
'https://www.tme.eu/en/details/smd0603-5k1-1%25/smd-resistors/royalohm/0603saf5101t5e/',
|
}
|
||||||
$results[0]->provider_url
|
|
||||||
);
|
/**
|
||||||
|
* Products can be missing most optional fields (category, manufacturer, symbols, images, EAN, ...).
|
||||||
|
* The provider must not crash on these and just return null for the corresponding DTO fields.
|
||||||
|
*/
|
||||||
|
public function testSearchByKeywordWithNullableFields(): void
|
||||||
|
{
|
||||||
|
$this->httpClient->setResponseFactory([
|
||||||
|
$this->mockTokenResponse(),
|
||||||
|
$this->mockSearchResults([[
|
||||||
|
'symbol' => 'FAKE_PRODUCT',
|
||||||
|
'description' => 'High-Quality bleeding edge fake product',
|
||||||
|
'product_status' => ['INVALID'],
|
||||||
|
]]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$results = $this->provider->searchByKeyword('FAKE_PRODUCT');
|
||||||
|
|
||||||
|
$this->assertCount(1, $results);
|
||||||
|
$this->assertSame('FAKE_PRODUCT', $results[0]->provider_id);
|
||||||
|
$this->assertSame('FAKE_PRODUCT', $results[0]->name);
|
||||||
|
$this->assertSame('High-Quality bleeding edge fake product', $results[0]->description);
|
||||||
|
$this->assertNull($results[0]->category);
|
||||||
|
$this->assertNull($results[0]->manufacturer);
|
||||||
|
$this->assertNull($results[0]->mpn);
|
||||||
|
$this->assertNull($results[0]->preview_image_url);
|
||||||
|
$this->assertNull($results[0]->gtin);
|
||||||
|
$this->assertSame(ManufacturingStatus::DISCONTINUED, $results[0]->manufacturing_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetDetailsWithPercentInPartNumber(): void
|
public function testGetDetailsWithPercentInPartNumber(): void
|
||||||
|
|
@ -385,16 +447,17 @@ final class TMEProviderTest extends TestCase
|
||||||
$this->assertSame('ROYALOHM', $result->manufacturer);
|
$this->assertSame('ROYALOHM', $result->manufacturer);
|
||||||
$this->assertSame('0603SAF5101T5E', $result->mpn);
|
$this->assertSame('0603SAF5101T5E', $result->mpn);
|
||||||
$this->assertSame('SMD resistors', $result->category);
|
$this->assertSame('SMD resistors', $result->category);
|
||||||
|
$this->assertSame('978020137962', $result->gtin);
|
||||||
$this->assertSame(ManufacturingStatus::ACTIVE, $result->manufacturing_status);
|
$this->assertSame(ManufacturingStatus::ACTIVE, $result->manufacturing_status);
|
||||||
$this->assertSame(0.021, $result->mass);
|
$this->assertSame(0.021, $result->mass);
|
||||||
$this->assertSame('1608', $result->footprint);
|
$this->assertSame('1608', $result->footprint);
|
||||||
$this->assertSame(
|
$this->assertSame('https://www.tme.eu/de/en/details/SMD0603-5K1-1%/', $result->provider_url);
|
||||||
'https://www.tme.eu/en/details/smd0603-5k1-1%25/smd-resistors/royalohm/0603saf5101t5e/',
|
|
||||||
$result->provider_url
|
|
||||||
);
|
|
||||||
|
|
||||||
|
// The firmware document (type SFT) must be filtered out, only the 2 DTE documents remain
|
||||||
$this->assertCount(2, $result->datasheets);
|
$this->assertCount(2, $result->datasheets);
|
||||||
|
$this->assertInstanceOf(FileDTO::class, $result->datasheets[0]);
|
||||||
$this->assertSame('https://www.tme.eu/Document/b315665a56acbc42df513c99b390ad98/ROYALOHM-THICKFILM.pdf', $result->datasheets[0]->url);
|
$this->assertSame('https://www.tme.eu/Document/b315665a56acbc42df513c99b390ad98/ROYALOHM-THICKFILM.pdf', $result->datasheets[0]->url);
|
||||||
|
$this->assertSame('ROYALOHM-THICKFILM.pdf', $result->datasheets[0]->name);
|
||||||
$this->assertCount(0, $result->images);
|
$this->assertCount(0, $result->images);
|
||||||
|
|
||||||
$this->assertCount(1, $result->vendor_infos);
|
$this->assertCount(1, $result->vendor_infos);
|
||||||
|
|
@ -402,10 +465,7 @@ final class TMEProviderTest extends TestCase
|
||||||
$this->assertInstanceOf(PurchaseInfoDTO::class, $vendorInfo);
|
$this->assertInstanceOf(PurchaseInfoDTO::class, $vendorInfo);
|
||||||
$this->assertSame('TME', $vendorInfo->distributor_name);
|
$this->assertSame('TME', $vendorInfo->distributor_name);
|
||||||
$this->assertSame('SMD0603-5K1-1%', $vendorInfo->order_number);
|
$this->assertSame('SMD0603-5K1-1%', $vendorInfo->order_number);
|
||||||
$this->assertSame(
|
$this->assertSame('https://www.tme.eu/de/en/details/SMD0603-5K1-1%/', $vendorInfo->product_url);
|
||||||
'https://www.tme.eu/en/details/smd0603-5k1-1%25/smd-resistors/royalohm/0603saf5101t5e/',
|
|
||||||
$vendorInfo->product_url
|
|
||||||
);
|
|
||||||
$this->assertCount(3, $vendorInfo->prices);
|
$this->assertCount(3, $vendorInfo->prices);
|
||||||
$this->assertSame(100.0, $vendorInfo->prices[0]->minimum_discount_amount);
|
$this->assertSame(100.0, $vendorInfo->prices[0]->minimum_discount_amount);
|
||||||
$this->assertSame('0.01077', $vendorInfo->prices[0]->price);
|
$this->assertSame('0.01077', $vendorInfo->prices[0]->price);
|
||||||
|
|
@ -436,20 +496,25 @@ final class TMEProviderTest extends TestCase
|
||||||
$this->assertSame('PANASONIC', $result->manufacturer);
|
$this->assertSame('PANASONIC', $result->manufacturer);
|
||||||
$this->assertSame('ETQP3M6R8KVP', $result->mpn);
|
$this->assertSame('ETQP3M6R8KVP', $result->mpn);
|
||||||
$this->assertSame('Inductors', $result->category);
|
$this->assertSame('Inductors', $result->category);
|
||||||
|
$this->assertNull($result->gtin);
|
||||||
$this->assertSame(ManufacturingStatus::ACTIVE, $result->manufacturing_status);
|
$this->assertSame(ManufacturingStatus::ACTIVE, $result->manufacturing_status);
|
||||||
$this->assertSame(0.44, $result->mass);
|
$this->assertSame(0.44, $result->mass);
|
||||||
$this->assertNull($result->footprint);
|
$this->assertNull($result->footprint);
|
||||||
$this->assertSame('https://www.tme.eu/en/details/etqp3m6r8kvp/inductors/panasonic/', $result->provider_url);
|
$this->assertSame('https://www.tme.eu/de/en/details/ETQP3M6R8KVP/', $result->provider_url);
|
||||||
|
|
||||||
$this->assertCount(2, $result->datasheets);
|
$this->assertCount(2, $result->datasheets);
|
||||||
$this->assertSame('https://www.tme.eu/Document/50a845881f09d8a2248350946e11df38/AGL0000C63.pdf', $result->datasheets[0]->url);
|
$this->assertSame('https://www.tme.eu/Document/50a845881f09d8a2248350946e11df38/AGL0000C63.pdf', $result->datasheets[0]->url);
|
||||||
$this->assertCount(0, $result->images);
|
|
||||||
|
// The additional image with a high_resolution variant must prefer it over "prime"
|
||||||
|
$this->assertCount(2, $result->images);
|
||||||
|
$this->assertSame('https://ce8dc832c.cloudimg.io/v7/_cdn_/additional1_prime.jpg', $result->images[0]->url);
|
||||||
|
$this->assertSame('https://ce8dc832c.cloudimg.io/v7/_cdn_/additional2_high_res.jpg', $result->images[1]->url);
|
||||||
|
|
||||||
$this->assertCount(1, $result->vendor_infos);
|
$this->assertCount(1, $result->vendor_infos);
|
||||||
$vendorInfo = $result->vendor_infos[0];
|
$vendorInfo = $result->vendor_infos[0];
|
||||||
$this->assertSame('TME', $vendorInfo->distributor_name);
|
$this->assertSame('TME', $vendorInfo->distributor_name);
|
||||||
$this->assertSame('ETQP3M6R8KVP', $vendorInfo->order_number);
|
$this->assertSame('ETQP3M6R8KVP', $vendorInfo->order_number);
|
||||||
$this->assertSame('https://www.tme.eu/en/details/etqp3m6r8kvp/inductors/panasonic/', $vendorInfo->product_url);
|
$this->assertSame('https://www.tme.eu/de/en/details/ETQP3M6R8KVP/', $vendorInfo->product_url);
|
||||||
$this->assertCount(3, $vendorInfo->prices);
|
$this->assertCount(3, $vendorInfo->prices);
|
||||||
$this->assertSame(1.0, $vendorInfo->prices[0]->minimum_discount_amount);
|
$this->assertSame(1.0, $vendorInfo->prices[0]->minimum_discount_amount);
|
||||||
$this->assertSame('0.589', $vendorInfo->prices[0]->price);
|
$this->assertSame('0.589', $vendorInfo->prices[0]->price);
|
||||||
|
|
@ -459,6 +524,67 @@ final class TMEProviderTest extends TestCase
|
||||||
$this->assertCount(3, $result->parameters);
|
$this->assertCount(3, $result->parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function footprintTestFixture(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
$this->mockTokenResponse(),
|
||||||
|
$this->mockProductsList([[
|
||||||
|
'symbol' => 'FAKE_PRODUCT',
|
||||||
|
'description' => 'Really nice fake product',
|
||||||
|
'product_status' => [],
|
||||||
|
]]),
|
||||||
|
$this->mockFilesList([[
|
||||||
|
'symbol' => 'FAKE_PRODUCT',
|
||||||
|
'documents' => ['elements' => []],
|
||||||
|
'assets' => ['additional' => ['elements' => []]],
|
||||||
|
]]),
|
||||||
|
$this->mockParametersList([[
|
||||||
|
'symbol' => 'FAKE_PRODUCT',
|
||||||
|
'parameters' => [
|
||||||
|
'elements' => [
|
||||||
|
['id' => 2932, 'name' => 'Case - inch', 'values' => [['value' => 'footprint_imperial']]],
|
||||||
|
['id' => 2931, 'name' => 'Case - mm', 'values' => [['value' => 'footprint_metric']]],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]]),
|
||||||
|
$this->mockPrices('EUR', 'NET', [[
|
||||||
|
'symbol' => 'FAKE_PRODUCT',
|
||||||
|
'prices' => ['currency' => 'EUR', 'type' => 'NET', 'elements' => []],
|
||||||
|
]]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetDetailsPrefersImperialFootprintWhenConfigured(): void
|
||||||
|
{
|
||||||
|
$this->httpClient->setResponseFactory($this->footprintTestFixture());
|
||||||
|
$this->settings->preferMetricFootprint = false;
|
||||||
|
|
||||||
|
$result = $this->provider->getDetails('FAKE_PRODUCT');
|
||||||
|
|
||||||
|
$this->assertSame('footprint_imperial', $result->footprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetDetailsPrefersMetricFootprintWhenConfigured(): void
|
||||||
|
{
|
||||||
|
$this->httpClient->setResponseFactory($this->footprintTestFixture());
|
||||||
|
$this->settings->preferMetricFootprint = true;
|
||||||
|
|
||||||
|
$result = $this->provider->getDetails('FAKE_PRODUCT');
|
||||||
|
|
||||||
|
$this->assertSame('footprint_metric', $result->footprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testProductStatusArrayToManufacturingStatus(): void
|
||||||
|
{
|
||||||
|
$method = (new \ReflectionClass($this->provider))->getMethod('productStatusArrayToManufacturingStatus');
|
||||||
|
|
||||||
|
$this->assertSame(ManufacturingStatus::ACTIVE, $method->invoke($this->provider, []));
|
||||||
|
$this->assertSame(ManufacturingStatus::DISCONTINUED, $method->invoke($this->provider, ['INVALID']));
|
||||||
|
$this->assertSame(ManufacturingStatus::DISCONTINUED, $method->invoke($this->provider, ['PRODUCT_BLOCKED']));
|
||||||
|
$this->assertSame(ManufacturingStatus::DISCONTINUED, $method->invoke($this->provider, ['NOT_IN_OFFER']));
|
||||||
|
$this->assertSame(ManufacturingStatus::EOL, $method->invoke($this->provider, ['AVAILABLE_WHILE_STOCKS_LAST']));
|
||||||
|
}
|
||||||
|
|
||||||
public function testNormalizeURLEncodesBarePctSign(): void
|
public function testNormalizeURLEncodesBarePctSign(): void
|
||||||
{
|
{
|
||||||
$method = (new \ReflectionClass($this->provider))->getMethod('normalizeURL');
|
$method = (new \ReflectionClass($this->provider))->getMethod('normalizeURL');
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue