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); } }