SuperSeller3000/tests/Integration/Infrastructure/Channel/Ebay/EbayAdapterIntegrationTest.php
Simon Kuehn 68a9f0094e feat: eBay sandbox integration — env config + taxonomy/adapter tests
Add sandbox credentials to .env.test and .env.local (sandbox URLs).
Pass EBAY_* vars through bin/test-integration.

EbayTaxonomyIntegrationTest: 6 tests against sandbox Taxonomy API using
app token (client_credentials) — verifies OAuth, aspects for notebooks
(cat 177) and RAM (cat 170083), required flags, value lists, caching.

EbayAdapterIntegrationTest: listing publish/update/deactivate tests skip
gracefully when EBAY_USER_TOKEN not set (Inventory API requires
Authorization Code user token). Noop-deactivate test always runs.

All 6 taxonomy tests pass against live sandbox.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 18:02:49 +00:00

155 lines
5.4 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\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;
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();
$oauth = new EbayOAuthClient(
$http,
$cache,
(string) $clientId,
(string) $clientSecret,
(string) $oauthBaseUrl,
);
$this->apiClient = new EbayInventoryApiClient(
$http,
$oauth,
(string) $apiBaseUrl,
(string) $marketplaceId,
);
$this->adapter = new EbayAdapter($this->apiClient);
$userToken = $_SERVER['EBAY_USER_TOKEN'] ?? getenv('EBAY_USER_TOKEN');
$this->userTokenAvailable = (bool) $userToken;
// Build a realistic sandbox article for listing tests
$type = new ArticleType('Notebook');
$this->article = new Article(
$type,
'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 test_publish_listing_creates_live_sandbox_listing(): 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 test_update_stock_changes_quantity(): 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 test_deactivate_listing_withdraws_offer(): 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 test_deactivate_listing_is_noop_when_no_listing_id(): void
{
// No listing ID set — should silently return, no API call
$this->adapter->deactivateListing($this->article);
$this->addToAssertionCount(1);
}
}