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