Files
orderPRO/tests/Unit/AllegroOrderImportServiceTest.php
Jacek Pyziak 5ab87a5a20 feat(07-pre-expansion-fixes): complete phase 07 — milestone v0.2 done
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>
2026-03-15 00:37:21 +01:00

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',
];
}
}