createMock(ProductPurchasePriceRepository::class); $upstream->expects($this->never())->method('mirrorBatch'); $mirror = $this->createMock(ProductPurchasePriceMirrorRepository::class); $mirror->method('available')->willReturn(true); $mirror->method('state')->willReturn(null); $result = (new ProductPurchasePriceMirrorSynchronizer($upstream, $mirror))->sync(); $this->assertSame('requires_full_sync', $result['status']); } public function test_incremental_sync_requires_a_fresh_full_pass_for_the_online_daifa_projection(): void { $upstream = $this->createMock(ProductPurchasePriceRepository::class); $upstream->expects($this->never())->method('mirrorBatch'); $mirror = $this->createMock(ProductPurchasePriceMirrorRepository::class); $mirror->method('available')->willReturn(true); $mirror->method('state')->willReturn([ 'initial_completed_at' => '2026-09-20 04:10:03', 'projection_version' => 1, ]); $result = (new ProductPurchasePriceMirrorSynchronizer($upstream, $mirror))->sync(); $this->assertSame('requires_full_sync', $result['status']); } public function test_full_sync_uses_stable_update_time_and_item_id_cursor(): void { Carbon::setTestNow('2026-08-21 12:00:00'); $rows = [[ 'item_id' => 100, 'purchase_order_id' => 827380, 'sku_id' => 'DF-A1DNZ001203', 'purchase_updated_at' => '2026-08-20T11:03:32.000', ], [ 'item_id' => 101, 'purchase_order_id' => 827381, 'sku_id' => 'DF-G1DNZ002401', 'purchase_updated_at' => '2026-08-20T11:03:32.000', ]]; $upstream = $this->createMock(ProductPurchasePriceRepository::class); $upstream->expects($this->exactly(2))->method('mirrorBatch') ->willReturnOnConsecutiveCalls($rows, []); $mirror = $this->createMock(ProductPurchasePriceMirrorRepository::class); $mirror->method('available')->willReturn(true); $mirror->method('state')->willReturn(null); $mirror->expects($this->once())->method('startRun') ->with( 'full', $this->isType('string'), $this->isInstanceOf(Carbon::class), $this->isInstanceOf(Carbon::class), false, ) ->willReturn(['last_upserted_rows' => 0]); $mirror->expects($this->once())->method('storeBatch') ->with( $rows, ['updated_at' => '2026-08-20T11:03:32.000', 'item_id' => 101], $this->isType('string'), 2, ); $mirror->expects($this->once())->method('complete') ->with('full', $this->isType('string'), $this->isInstanceOf(Carbon::class), 2); $result = (new ProductPurchasePriceMirrorSynchronizer($upstream, $mirror))->sync(true, 2, 0); $this->assertSame([ 'status' => 'succeeded', 'mode' => 'full', 'batches' => 1, 'rows' => 2, ], $result); } }