Phase 7 complete (5 plans): - 07-01: Performance (N+1→LEFT JOIN, static cache, DB indexes) - 07-02: Stability (SSL verification, cron throttle DB, migration 000014b) - 07-03: UX (orderpro_to_allegro disable, lista zamówień fixes, SSL hotfix) - 07-04: Tests (12 unit tests for AllegroTokenManager + AllegroOrderImportService) - 07-05: InPost ShipX API (natywny provider, workaround remap usunięty) Additional fixes: - 5 broken use-statements fixed across 4 files - vendor/ excluded from ftp-kr auto-upload - PHPUnit + dg/bypass-finals infrastructure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
231 lines
8.2 KiB
PHP
231 lines
8.2 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Unit;
|
|
|
|
use App\Core\Exceptions\AllegroApiException;
|
|
use App\Modules\Orders\OrderImportRepository;
|
|
use App\Modules\Orders\OrdersRepository;
|
|
use App\Modules\Settings\AllegroApiClient;
|
|
use App\Modules\Settings\AllegroIntegrationRepository;
|
|
use App\Modules\Settings\AllegroOrderImportService;
|
|
use App\Modules\Settings\AllegroStatusMappingRepository;
|
|
use App\Modules\Settings\AllegroTokenManager;
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
|
use PHPUnit\Framework\TestCase;
|
|
use RuntimeException;
|
|
|
|
final class AllegroOrderImportServiceTest extends TestCase
|
|
{
|
|
private AllegroIntegrationRepository&MockObject $integrationRepository;
|
|
private AllegroTokenManager&MockObject $tokenManager;
|
|
private AllegroApiClient&MockObject $apiClient;
|
|
private OrderImportRepository&MockObject $orders;
|
|
private AllegroStatusMappingRepository&MockObject $statusMappings;
|
|
private OrdersRepository&MockObject $ordersRepository;
|
|
private AllegroOrderImportService $service;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->integrationRepository = $this->createMock(AllegroIntegrationRepository::class);
|
|
$this->tokenManager = $this->createMock(AllegroTokenManager::class);
|
|
$this->apiClient = $this->createMock(AllegroApiClient::class);
|
|
$this->orders = $this->createMock(OrderImportRepository::class);
|
|
$this->statusMappings = $this->createMock(AllegroStatusMappingRepository::class);
|
|
$this->ordersRepository = $this->createMock(OrdersRepository::class);
|
|
|
|
$this->service = new AllegroOrderImportService(
|
|
$this->integrationRepository,
|
|
$this->tokenManager,
|
|
$this->apiClient,
|
|
$this->orders,
|
|
$this->statusMappings,
|
|
$this->ordersRepository
|
|
);
|
|
}
|
|
|
|
public function testImportSingleOrderHappyPath(): void
|
|
{
|
|
$checkoutFormId = 'abc-123';
|
|
$payload = $this->buildMinimalPayload($checkoutFormId);
|
|
|
|
$this->tokenManager
|
|
->method('resolveToken')
|
|
->willReturn(['access-token-xyz', 'sandbox']);
|
|
|
|
$this->apiClient
|
|
->method('getCheckoutForm')
|
|
->with('sandbox', 'access-token-xyz', $checkoutFormId)
|
|
->willReturn($payload);
|
|
|
|
$this->apiClient
|
|
->method('getCheckoutFormShipments')
|
|
->willReturn([]);
|
|
|
|
$this->integrationRepository
|
|
->method('getActiveIntegrationId')
|
|
->willReturn(5);
|
|
|
|
$this->statusMappings
|
|
->method('findMappedOrderproStatusCode')
|
|
->willReturn('new');
|
|
|
|
$this->orders
|
|
->expects($this->once())
|
|
->method('upsertOrderAggregate')
|
|
->willReturn(['order_id' => 42, 'created' => true]);
|
|
|
|
$this->ordersRepository
|
|
->expects($this->once())
|
|
->method('recordActivity')
|
|
->with(42, 'import', $this->stringContains('Zaimportowano'));
|
|
|
|
$result = $this->service->importSingleOrder($checkoutFormId);
|
|
|
|
$this->assertSame(42, $result['order_id']);
|
|
$this->assertTrue($result['created']);
|
|
$this->assertSame($checkoutFormId, $result['source_order_id']);
|
|
}
|
|
|
|
public function testImportSingleOrderRetryOn401(): void
|
|
{
|
|
$checkoutFormId = 'retry-456';
|
|
$payload = $this->buildMinimalPayload($checkoutFormId);
|
|
|
|
$this->tokenManager
|
|
->method('resolveToken')
|
|
->willReturnOnConsecutiveCalls(
|
|
['stale-token', 'sandbox'],
|
|
['fresh-token', 'sandbox']
|
|
);
|
|
|
|
$callCount = 0;
|
|
$this->apiClient
|
|
->method('getCheckoutForm')
|
|
->willReturnCallback(function () use (&$callCount, $payload): array {
|
|
$callCount++;
|
|
if ($callCount === 1) {
|
|
throw new RuntimeException('ALLEGRO_HTTP_401');
|
|
}
|
|
return $payload;
|
|
});
|
|
|
|
$this->apiClient->method('getCheckoutFormShipments')->willReturn([]);
|
|
$this->integrationRepository->method('getActiveIntegrationId')->willReturn(1);
|
|
$this->statusMappings->method('findMappedOrderproStatusCode')->willReturn(null);
|
|
|
|
$this->orders
|
|
->expects($this->once())
|
|
->method('upsertOrderAggregate')
|
|
->willReturn(['order_id' => 99, 'created' => true]);
|
|
|
|
$result = $this->service->importSingleOrder($checkoutFormId);
|
|
|
|
$this->assertSame(99, $result['order_id']);
|
|
$this->assertSame(2, $callCount);
|
|
}
|
|
|
|
public function testImportSingleOrderThrowsOnEmptyId(): void
|
|
{
|
|
$this->expectException(AllegroApiException::class);
|
|
$this->expectExceptionMessage('Podaj ID zamowienia');
|
|
|
|
$this->service->importSingleOrder('');
|
|
}
|
|
|
|
public function testImportSingleOrderPropagatesNon401RuntimeException(): void
|
|
{
|
|
$this->tokenManager
|
|
->method('resolveToken')
|
|
->willReturn(['token', 'sandbox']);
|
|
|
|
$this->apiClient
|
|
->method('getCheckoutForm')
|
|
->willThrowException(new RuntimeException('ALLEGRO_HTTP_500'));
|
|
|
|
$this->expectException(RuntimeException::class);
|
|
$this->expectExceptionMessage('ALLEGRO_HTTP_500');
|
|
|
|
$this->service->importSingleOrder('order-789');
|
|
}
|
|
|
|
public function testImportSingleOrderReturnsCorrectFieldsOnReImport(): void
|
|
{
|
|
$checkoutFormId = 'reimport-001';
|
|
$payload = $this->buildMinimalPayload($checkoutFormId);
|
|
|
|
$this->tokenManager->method('resolveToken')->willReturn(['tok', 'production']);
|
|
$this->apiClient->method('getCheckoutForm')->willReturn($payload);
|
|
$this->apiClient->method('getCheckoutFormShipments')->willReturn([]);
|
|
$this->integrationRepository->method('getActiveIntegrationId')->willReturn(1);
|
|
$this->statusMappings->method('findMappedOrderproStatusCode')->willReturn(null);
|
|
|
|
$this->orders
|
|
->method('upsertOrderAggregate')
|
|
->willReturn(['order_id' => 10, 'created' => false]);
|
|
|
|
$this->ordersRepository
|
|
->expects($this->once())
|
|
->method('recordActivity')
|
|
->with(10, 'import', $this->stringContains('Zaktualizowano'));
|
|
|
|
$result = $this->service->importSingleOrder($checkoutFormId);
|
|
|
|
$this->assertSame(10, $result['order_id']);
|
|
$this->assertFalse($result['created']);
|
|
$this->assertArrayHasKey('image_diagnostics', $result);
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
private function buildMinimalPayload(string $checkoutFormId): array
|
|
{
|
|
return [
|
|
'id' => $checkoutFormId,
|
|
'status' => 'READY_FOR_PROCESSING',
|
|
'fulfillment' => ['status' => 'NEW'],
|
|
'payment' => [
|
|
'id' => 'pay-1',
|
|
'type' => 'allegro',
|
|
'status' => 'paid',
|
|
'amount' => ['amount' => '99.00', 'currency' => 'PLN'],
|
|
'paidAmount' => ['amount' => '99.00', 'currency' => 'PLN'],
|
|
],
|
|
'summary' => [
|
|
'totalToPay' => ['amount' => '99.00', 'currency' => 'PLN'],
|
|
],
|
|
'buyer' => [
|
|
'login' => 'test-buyer',
|
|
'firstName' => 'Jan',
|
|
'lastName' => 'Kowalski',
|
|
'email' => 'jan@example.com',
|
|
'phoneNumber' => '500100200',
|
|
],
|
|
'delivery' => [
|
|
'method' => ['id' => 'inpost', 'name' => 'InPost Paczkomaty'],
|
|
'address' => [
|
|
'firstName' => 'Jan',
|
|
'lastName' => 'Kowalski',
|
|
'street' => 'ul. Testowa 1',
|
|
'city' => 'Warszawa',
|
|
'zipCode' => '00-001',
|
|
'countryCode' => 'PL',
|
|
],
|
|
],
|
|
'lineItems' => [
|
|
[
|
|
'id' => 'item-1',
|
|
'offer' => ['id' => 'offer-1', 'name' => 'Produkt testowy'],
|
|
'quantity' => 2,
|
|
'originalPrice' => ['amount' => '49.50', 'currency' => 'PLN'],
|
|
],
|
|
],
|
|
'invoice' => [],
|
|
'marketplace' => ['id' => 'allegro-pl'],
|
|
'boughtAt' => '2026-03-14T10:00:00Z',
|
|
'updatedAt' => '2026-03-14T10:05:00Z',
|
|
];
|
|
}
|
|
}
|