Some checks are pending
CI / test (push) Waiting to run
- ArticleTypePlatformConfig: fulfillmentPolicyId, paymentPolicyId, returnPolicyId, merchantLocationKey (all nullable) - EbayAccountApiClient: fetches Fulfillment/Payment/Return policies from eBay Account API (/sell/account/v1) - EbayInventoryApiClient: adds getLocations() - EbayPolicyProvider: aggregates choices with 5 min cache; returns empty array on API failure so the form degrades to TextField - EbayAdapter: reads real ArticleTypePlatformConfig (category ID no longer hardcoded), passes listingPolicies + merchantLocationKey into createOffer() when set - EbayArticleTypePlatformConfigCrudController: live policy dropdowns from EbayPolicyProvider; fallback to TextField with help text - DashboardController: eBay subMenu with Kategorie-Konfigurationen - 7 new unit tests for EbayAdapter policy scenarios Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
160 lines
5.9 KiB
PHP
160 lines
5.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Integration\Infrastructure\Channel\Ebay;
|
|
|
|
use App\Domain\Article\Article;
|
|
use App\Domain\Article\ArticleCondition;
|
|
use App\Domain\Article\ArticleType;
|
|
use App\Domain\Channel\ArticleTypePlatformConfig;
|
|
use App\Domain\Channel\Platform;
|
|
use App\Domain\Channel\Repository\ArticleTypePlatformConfigRepositoryInterface;
|
|
use App\Infrastructure\Channel\Ebay\EbayAdapter;
|
|
use App\Infrastructure\Channel\Ebay\EbayInventoryApiClient;
|
|
use App\Infrastructure\Channel\Ebay\EbayOAuthClient;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
|
use Symfony\Component\HttpClient\HttpClient;
|
|
|
|
/**
|
|
* Integration tests for EbayAdapter against the eBay Sandbox.
|
|
*
|
|
* The Inventory/Offer APIs require a user-level OAuth token (Authorization Code
|
|
* grant with sell.inventory scope) — app token alone is insufficient.
|
|
*
|
|
* Set EBAY_USER_TOKEN in .env.local to enable listing tests:
|
|
* EBAY_USER_TOKEN=v^1.1#i^1#I^3#...
|
|
*
|
|
* Taxonomy / auth tests run with just the app token.
|
|
*
|
|
* Run with: bin/test-integration tests/Integration/Infrastructure/Channel/Ebay/
|
|
*/
|
|
final class EbayAdapterIntegrationTest extends TestCase
|
|
{
|
|
private EbayAdapter $adapter;
|
|
private EbayInventoryApiClient $apiClient;
|
|
private Article $article;
|
|
private string $createdListingId = '';
|
|
private bool $userTokenAvailable = false;
|
|
|
|
private static function env(string $key, string $default = ''): string
|
|
{
|
|
$val = $_SERVER[$key] ?? getenv($key);
|
|
|
|
return \is_string($val) ? $val : $default;
|
|
}
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$clientId = self::env('EBAY_CLIENT_ID');
|
|
$clientSecret = self::env('EBAY_CLIENT_SECRET');
|
|
$apiBaseUrl = self::env('EBAY_API_BASE_URL');
|
|
$oauthBaseUrl = self::env('EBAY_OAUTH_BASE_URL');
|
|
$marketplaceId = self::env('EBAY_MARKETPLACE_ID', 'EBAY_DE');
|
|
|
|
if ('' === $clientId || '' === $clientSecret || '' === $apiBaseUrl || '' === $oauthBaseUrl) {
|
|
$this->markTestSkipped('EBAY_* env vars not set');
|
|
}
|
|
|
|
$http = HttpClient::create();
|
|
$cache = new ArrayAdapter();
|
|
|
|
$oauth = new EbayOAuthClient($http, $cache, $clientId, $clientSecret, $oauthBaseUrl);
|
|
|
|
$this->apiClient = new EbayInventoryApiClient($http, $oauth, $apiBaseUrl, $marketplaceId);
|
|
|
|
$articleType = new ArticleType('Notebook');
|
|
$platform = new Platform('ebay', 'eBay');
|
|
// Sandbox category 177 = Notebooks; no policies needed for sandbox withdraw/stock tests
|
|
$platformConfig = new ArticleTypePlatformConfig($articleType, $platform, '177');
|
|
|
|
$configRepo = $this->createStub(ArticleTypePlatformConfigRepositoryInterface::class);
|
|
$configRepo->method('findByArticleTypeAndPlatformType')->willReturn($platformConfig);
|
|
|
|
$this->adapter = new EbayAdapter($this->apiClient, $configRepo, $marketplaceId);
|
|
|
|
$this->userTokenAvailable = '' !== self::env('EBAY_USER_TOKEN');
|
|
|
|
// Build a realistic sandbox article for listing tests (reuse same $articleType so config matches)
|
|
$this->article = new Article(
|
|
$articleType,
|
|
'SS3K-TEST-'.time(),
|
|
'INV-TEST-001',
|
|
1,
|
|
ArticleCondition::Good,
|
|
);
|
|
$this->article->setManufacturer('Lenovo');
|
|
$this->article->setModelName('ThinkBook 14 G6 IRL');
|
|
$this->article->setModelNumber('21KG00NQGE');
|
|
$this->article->setEbayTitle('Lenovo ThinkBook 14 G6 IRL — Sandbox Test');
|
|
$this->article->setEbayDescription('Integration test listing — do not bid.');
|
|
$this->article->setListingPrice('1.00');
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
if ('' !== $this->createdListingId) {
|
|
try {
|
|
$this->adapter->deactivateListing($this->article);
|
|
} catch (\Throwable) {
|
|
// best-effort cleanup
|
|
}
|
|
}
|
|
}
|
|
|
|
public function testPublishListingCreatesLiveSandboxListing(): void
|
|
{
|
|
if (!$this->userTokenAvailable) {
|
|
$this->markTestSkipped(
|
|
'EBAY_USER_TOKEN not set. Generate a sandbox user token via Authorization Code '.
|
|
'flow (sell.inventory scope) and add it to .env.local to enable this test.'
|
|
);
|
|
}
|
|
|
|
$listingId = $this->adapter->publishListing($this->article);
|
|
|
|
$this->assertNotEmpty($listingId);
|
|
$this->createdListingId = $listingId;
|
|
|
|
// Store so deactivateListing in tearDown finds it
|
|
$this->article->setEbayListingId($listingId);
|
|
}
|
|
|
|
public function testUpdateStockChangesQuantity(): void
|
|
{
|
|
if (!$this->userTokenAvailable) {
|
|
$this->markTestSkipped('EBAY_USER_TOKEN not set — see test_publish_listing_creates_live_sandbox_listing');
|
|
}
|
|
|
|
// First publish to create the item
|
|
$this->adapter->publishListing($this->article);
|
|
$this->article->setEbayListingId($this->createdListingId);
|
|
|
|
// Should not throw
|
|
$this->adapter->updateStock($this->article, 2);
|
|
$this->addToAssertionCount(1);
|
|
}
|
|
|
|
public function testDeactivateListingWithdrawsOffer(): void
|
|
{
|
|
if (!$this->userTokenAvailable) {
|
|
$this->markTestSkipped('EBAY_USER_TOKEN not set — see test_publish_listing_creates_live_sandbox_listing');
|
|
}
|
|
|
|
$listingId = $this->adapter->publishListing($this->article);
|
|
$this->article->setEbayListingId($listingId);
|
|
|
|
// Should not throw
|
|
$this->adapter->deactivateListing($this->article);
|
|
$this->createdListingId = ''; // already deactivated, skip tearDown cleanup
|
|
$this->addToAssertionCount(1);
|
|
}
|
|
|
|
public function testDeactivateListingIsNoopWhenNoListingId(): void
|
|
{
|
|
// No listing ID set — should silently return, no API call
|
|
$this->adapter->deactivateListing($this->article);
|
|
$this->addToAssertionCount(1);
|
|
}
|
|
}
|