SuperSeller3000/tests/Integration/Infrastructure/Channel/Ebay/EbayTaxonomyIntegrationTest.php

154 lines
5.9 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
namespace App\Tests\Integration\Infrastructure\Channel\Ebay;
use App\Infrastructure\Channel\Ebay\EbayOAuthClient;
use App\Infrastructure\Channel\Ebay\EbayTaxonomyService;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\HttpClient\HttpClient;
/**
* Integration tests against the eBay Sandbox Taxonomy API.
* Uses application token (client_credentials) no user OAuth needed.
*
* Requires EBAY_CLIENT_ID, EBAY_CLIENT_SECRET, EBAY_API_BASE_URL,
* EBAY_OAUTH_BASE_URL, EBAY_MARKETPLACE_ID in .env.local.
*
* Run with: bin/test-integration tests/Integration/Infrastructure/Channel/Ebay/
*/
final class EbayTaxonomyIntegrationTest extends TestCase
{
private EbayTaxonomyService $taxonomy;
private EbayOAuthClient $oauth;
protected function setUp(): void
{
$clientId = $_SERVER['EBAY_CLIENT_ID'] ?? getenv('EBAY_CLIENT_ID');
$clientSecret = $_SERVER['EBAY_CLIENT_SECRET'] ?? getenv('EBAY_CLIENT_SECRET');
$apiBaseUrl = $_SERVER['EBAY_API_BASE_URL'] ?? getenv('EBAY_API_BASE_URL');
$oauthBaseUrl = $_SERVER['EBAY_OAUTH_BASE_URL'] ?? getenv('EBAY_OAUTH_BASE_URL');
$marketplaceId = $_SERVER['EBAY_MARKETPLACE_ID'] ?? getenv('EBAY_MARKETPLACE_ID') ?: 'EBAY_DE';
if (!$clientId || !$clientSecret || !$apiBaseUrl || !$oauthBaseUrl) {
$this->markTestSkipped('EBAY_* env vars not set');
}
$http = HttpClient::create();
$cache = new ArrayAdapter();
$this->oauth = new EbayOAuthClient(
$http,
$cache,
(string) $clientId,
(string) $clientSecret,
(string) $oauthBaseUrl,
);
$this->taxonomy = new EbayTaxonomyService(
$http,
$this->oauth,
$cache,
(string) $apiBaseUrl,
(string) $marketplaceId,
);
}
public function test_fetches_application_token(): void
{
$token = $this->oauth->getAccessToken();
$this->assertNotEmpty($token);
$this->assertStringStartsWith('v^1.1', $token);
}
public function test_fetches_aspects_for_notebooks_category(): void
{
// 177 = Notebooks & Netbooks in EBAY_DE
$aspects = $this->taxonomy->getCategoryAspects('177');
$this->assertNotEmpty($aspects, 'Category 177 should have aspects');
foreach ($aspects as $aspect) {
$this->assertArrayHasKey('name', $aspect);
$this->assertArrayHasKey('required', $aspect);
$this->assertArrayHasKey('usage', $aspect);
$this->assertArrayHasKey('values', $aspect);
$this->assertIsString($aspect['name']);
$this->assertIsBool($aspect['required']);
$this->assertContains($aspect['usage'], ['RECOMMENDED', 'OPTIONAL'], 'usage must be a known eBay tier');
$this->assertIsArray($aspect['values']);
}
$names = array_column($aspects, 'name');
$this->assertNotEmpty(
array_filter($names, static fn (string $n) => str_contains(strtolower($n), 'prozes') || str_contains(strtolower($n), 'cpu') || str_contains(strtolower($n), 'speicher')),
'Expected at least one hardware aspect (CPU/RAM)',
);
}
public function test_three_tier_aspect_classification(): void
{
// eBay has three effective tiers:
// required=true + usage=RECOMMENDED → hard gate, eBay blocks listing without it
// required=false + usage=RECOMMENDED → "you should have this", search ranking signal
// required=false + usage=OPTIONAL → truly optional, low impact
$aspects = $this->taxonomy->getCategoryAspects('177');
$hardRequired = array_filter($aspects, static fn (array $a) => $a['required']);
$recommended = array_filter($aspects, static fn (array $a) => !$a['required'] && 'RECOMMENDED' === $a['usage']);
$optional = array_filter($aspects, static fn (array $a) => !$a['required'] && 'OPTIONAL' === $a['usage']);
$this->assertNotEmpty($hardRequired, 'Should have hard-required aspects');
$this->assertNotEmpty($recommended, 'Should have recommended (ranking-signal) aspects');
$this->assertNotEmpty($optional, 'Should have truly optional aspects');
}
public function test_aspects_with_predefined_values_have_options(): void
{
$aspects = $this->taxonomy->getCategoryAspects('177');
$withOptions = array_filter($aspects, static fn (array $a) => [] !== $a['values']);
$this->assertNotEmpty($withOptions, 'Some aspects should have predefined selectable values');
}
public function test_caches_aspects_on_second_call(): void
{
// First call hits the API
$first = $this->taxonomy->getCategoryAspects('177');
// Second call should return from cache (same ArrayAdapter instance)
$second = $this->taxonomy->getCategoryAspects('177');
$this->assertSame($first, $second);
}
public function test_fetches_aspects_for_ram_category(): void
{
// 170083 = RAM/Speicher in EBAY_DE — useful for our memory article types
$aspects = $this->taxonomy->getCategoryAspects('170083');
$this->assertNotEmpty($aspects);
$names = array_map(static fn (array $a) => $a['name'], $aspects);
$this->assertNotEmpty($names);
}
public function test_category_suggestions_returns_results(): void
{
$results = $this->taxonomy->getCategorySuggestions('Notebook');
$this->assertNotEmpty($results, 'Sandbox must return at least one suggestion for "Notebook"');
foreach ($results as $result) {
$this->assertArrayHasKey('id', $result);
$this->assertArrayHasKey('name', $result);
$this->assertArrayHasKey('path', $result);
$this->assertIsString($result['id']);
$this->assertNotEmpty($result['id']);
$this->assertIsString($result['name']);
}
}
}