265 lines
9.0 KiB
PHP
265 lines
9.0 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Unit;
|
|
|
|
use App\Modules\Cron\CronRepository;
|
|
use App\Modules\Settings\AllegroApiClient;
|
|
use App\Modules\Settings\AllegroIntegrationRepository;
|
|
use App\Modules\Settings\AllegroOrderImportService;
|
|
use App\Modules\Settings\AllegroOrderSyncStateRepository;
|
|
use App\Modules\Settings\AllegroStatusMappingRepository;
|
|
use App\Modules\Settings\AllegroStatusSyncService;
|
|
use App\Modules\Settings\AllegroTokenManager;
|
|
use PDO;
|
|
use PDOStatement;
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
|
use PHPUnit\Framework\TestCase;
|
|
use RuntimeException;
|
|
|
|
final class AllegroStatusSyncServiceTest extends TestCase
|
|
{
|
|
private CronRepository&MockObject $cronRepository;
|
|
private AllegroOrderImportService&MockObject $orderImportService;
|
|
private AllegroApiClient&MockObject $apiClient;
|
|
private AllegroTokenManager&MockObject $tokenManager;
|
|
private AllegroStatusMappingRepository&MockObject $statusMappings;
|
|
private AllegroOrderSyncStateRepository&MockObject $syncStateRepository;
|
|
private AllegroIntegrationRepository&MockObject $integrationRepository;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->cronRepository = $this->createMock(CronRepository::class);
|
|
$this->orderImportService = $this->createMock(AllegroOrderImportService::class);
|
|
$this->apiClient = $this->createMock(AllegroApiClient::class);
|
|
$this->tokenManager = $this->createMock(AllegroTokenManager::class);
|
|
$this->statusMappings = $this->createMock(AllegroStatusMappingRepository::class);
|
|
$this->syncStateRepository = $this->createMock(AllegroOrderSyncStateRepository::class);
|
|
$this->integrationRepository = $this->createMock(AllegroIntegrationRepository::class);
|
|
}
|
|
|
|
public function testPushDirectionProcessesMappedOrdersAndSkipsUnmapped(): void
|
|
{
|
|
$this->cronRepository
|
|
->method('getStringSetting')
|
|
->willReturn('orderpro_to_allegro');
|
|
|
|
$this->integrationRepository
|
|
->method('getActiveIntegrationId')
|
|
->willReturn(5);
|
|
|
|
$this->statusMappings
|
|
->method('buildOrderproToAllegroMap')
|
|
->willReturn([
|
|
'processing' => 'ready_for_processing',
|
|
]);
|
|
|
|
$this->tokenManager
|
|
->method('resolveToken')
|
|
->willReturn(['token-1', 'sandbox']);
|
|
|
|
$this->syncStateRepository
|
|
->method('getLastStatusPushedAt')
|
|
->willReturn(null);
|
|
|
|
$this->syncStateRepository
|
|
->expects($this->once())
|
|
->method('updateLastStatusPushedAt')
|
|
->with(5, '2026-03-28 10:10:00');
|
|
|
|
$this->apiClient
|
|
->expects($this->exactly(2))
|
|
->method('updateCheckoutFormFulfillment')
|
|
->with(
|
|
'sandbox',
|
|
'token-1',
|
|
$this->logicalOr('A1', 'A3'),
|
|
'ready_for_processing'
|
|
)
|
|
->willReturn([]);
|
|
|
|
$service = $this->createServiceWithPushRows([
|
|
['source_order_id' => 'A1', 'orderpro_status_code' => 'processing', 'latest_change' => '2026-03-28 10:00:00'],
|
|
['source_order_id' => 'A2', 'orderpro_status_code' => 'unknown', 'latest_change' => '2026-03-28 10:05:00'],
|
|
['source_order_id' => 'A3', 'orderpro_status_code' => 'processing', 'latest_change' => '2026-03-28 10:10:00'],
|
|
]);
|
|
|
|
$result = $service->sync();
|
|
|
|
$this->assertTrue($result['ok']);
|
|
$this->assertSame('orderpro_to_allegro', $result['direction']);
|
|
$this->assertSame(2, $result['pushed']);
|
|
$this->assertSame(1, $result['skipped']);
|
|
$this->assertSame(0, $result['failed']);
|
|
}
|
|
|
|
public function testPushDirectionReturnsEarlyWhenNoMappingsExist(): void
|
|
{
|
|
$this->cronRepository
|
|
->method('getStringSetting')
|
|
->willReturn('orderpro_to_allegro');
|
|
|
|
$this->integrationRepository
|
|
->method('getActiveIntegrationId')
|
|
->willReturn(5);
|
|
|
|
$this->statusMappings
|
|
->method('buildOrderproToAllegroMap')
|
|
->willReturn([]);
|
|
|
|
$this->apiClient
|
|
->expects($this->never())
|
|
->method('updateCheckoutFormFulfillment');
|
|
|
|
$service = $this->createServiceWithPushRows([]);
|
|
$result = $service->sync();
|
|
|
|
$this->assertTrue($result['ok']);
|
|
$this->assertSame('orderpro_to_allegro', $result['direction']);
|
|
$this->assertSame(0, $result['pushed']);
|
|
$this->assertSame(0, $result['failed']);
|
|
$this->assertStringContainsString('Brak mapowan', (string) ($result['message'] ?? ''));
|
|
}
|
|
|
|
public function testPushDirectionCollectsFailureAndContinuesProcessing(): void
|
|
{
|
|
$this->cronRepository
|
|
->method('getStringSetting')
|
|
->willReturn('orderpro_to_allegro');
|
|
|
|
$this->integrationRepository
|
|
->method('getActiveIntegrationId')
|
|
->willReturn(7);
|
|
|
|
$this->statusMappings
|
|
->method('buildOrderproToAllegroMap')
|
|
->willReturn([
|
|
'processing' => 'ready_for_processing',
|
|
]);
|
|
|
|
$this->tokenManager
|
|
->method('resolveToken')
|
|
->willReturn(['token-fail-test', 'sandbox']);
|
|
|
|
$this->syncStateRepository
|
|
->method('getLastStatusPushedAt')
|
|
->willReturn(null);
|
|
|
|
$this->syncStateRepository
|
|
->expects($this->once())
|
|
->method('updateLastStatusPushedAt')
|
|
->with(7, '2026-03-28 10:10:00');
|
|
|
|
$calls = 0;
|
|
$this->apiClient
|
|
->method('updateCheckoutFormFulfillment')
|
|
->willReturnCallback(function () use (&$calls): array {
|
|
$calls++;
|
|
if ($calls === 1) {
|
|
throw new RuntimeException('API Allegro HTTP 422');
|
|
}
|
|
|
|
return [];
|
|
});
|
|
|
|
$service = $this->createServiceWithPushRows([
|
|
['source_order_id' => 'X1', 'orderpro_status_code' => 'processing', 'latest_change' => '2026-03-28 10:00:00'],
|
|
['source_order_id' => 'X2', 'orderpro_status_code' => 'processing', 'latest_change' => '2026-03-28 10:10:00'],
|
|
]);
|
|
|
|
$result = $service->sync();
|
|
|
|
$this->assertTrue($result['ok']);
|
|
$this->assertSame(1, $result['pushed']);
|
|
$this->assertSame(1, $result['failed']);
|
|
$this->assertCount(1, $result['errors']);
|
|
$this->assertSame('X1', $result['errors'][0]['source_order_id'] ?? null);
|
|
}
|
|
|
|
public function testPushDirectionRetriesOnceAfter401(): void
|
|
{
|
|
$this->cronRepository
|
|
->method('getStringSetting')
|
|
->willReturn('orderpro_to_allegro');
|
|
|
|
$this->integrationRepository
|
|
->method('getActiveIntegrationId')
|
|
->willReturn(9);
|
|
|
|
$this->statusMappings
|
|
->method('buildOrderproToAllegroMap')
|
|
->willReturn([
|
|
'processing' => 'ready_for_processing',
|
|
]);
|
|
|
|
$this->tokenManager
|
|
->method('resolveToken')
|
|
->willReturnOnConsecutiveCalls(
|
|
['token-old', 'sandbox'],
|
|
['token-new', 'sandbox']
|
|
);
|
|
|
|
$this->syncStateRepository
|
|
->method('getLastStatusPushedAt')
|
|
->willReturn(null);
|
|
|
|
$this->syncStateRepository
|
|
->expects($this->once())
|
|
->method('updateLastStatusPushedAt')
|
|
->with(9, '2026-03-28 11:00:00');
|
|
|
|
$calls = 0;
|
|
$this->apiClient
|
|
->method('updateCheckoutFormFulfillment')
|
|
->willReturnCallback(function () use (&$calls): array {
|
|
$calls++;
|
|
if ($calls === 1) {
|
|
throw new RuntimeException('ALLEGRO_HTTP_401');
|
|
}
|
|
|
|
return [];
|
|
});
|
|
|
|
$service = $this->createServiceWithPushRows([
|
|
['source_order_id' => 'R1', 'orderpro_status_code' => 'processing', 'latest_change' => '2026-03-28 11:00:00'],
|
|
]);
|
|
$result = $service->sync();
|
|
|
|
$this->assertTrue($result['ok']);
|
|
$this->assertSame(1, $result['pushed']);
|
|
$this->assertSame(0, $result['failed']);
|
|
$this->assertSame(0, $result['skipped']);
|
|
$this->assertSame(2, $calls);
|
|
}
|
|
|
|
/**
|
|
* @param array<int, array<string, mixed>> $rows
|
|
*/
|
|
private function createServiceWithPushRows(array $rows): AllegroStatusSyncService
|
|
{
|
|
$statement = $this->createMock(PDOStatement::class);
|
|
$statement
|
|
->method('execute')
|
|
->willReturn(true);
|
|
$statement
|
|
->method('fetchAll')
|
|
->willReturn($rows);
|
|
|
|
$pdo = $this->createMock(PDO::class);
|
|
$pdo
|
|
->method('prepare')
|
|
->willReturn($statement);
|
|
|
|
return new AllegroStatusSyncService(
|
|
$this->cronRepository,
|
|
$this->orderImportService,
|
|
$this->apiClient,
|
|
$this->tokenManager,
|
|
$this->statusMappings,
|
|
$this->syncStateRepository,
|
|
$this->integrationRepository,
|
|
$pdo
|
|
);
|
|
}
|
|
}
|