calls[] = compact('scope', 'parameters'); $this->value = $resolver(); return 'fresh'; } }; $caches = new class($cache) extends UpstreamQueryCacheFactory { public string $requestedSource = ''; public function __construct(private readonly UpstreamQueryCache $cache) { } public function forSource(string $source): UpstreamQueryCache { $this->requestedSource = $source; return $this->cache; } }; $information = new class extends UpstreamBusinessInformation { public array $calls = []; public function __construct() { } public function snapshot(string $scope, array $parameters): array { $this->calls[] = compact('scope', 'parameters'); return ['scope' => $scope, 'parameters' => $parameters]; } }; $orders = $this->createMock(OrderDataRepository::class); $productPrices = $this->createMock(ProductPriceRepository::class); $productPrices->expects($this->never())->method('paginate'); $refresher = new OperationsReplicaSnapshotRefresher($caches, $information, $orders, $productPrices); $this->assertSame('operations_replica', $refresher->sourceKey()); $this->assertSame('fresh', $refresher->refresh(compact('scope', 'parameters'))); $this->assertSame('operations_replica', $caches->requestedSource); $this->assertSame([compact('scope', 'parameters')], $cache->calls); $this->assertSame([compact('scope', 'parameters')], $information->calls); $this->assertSame(['scope' => $scope, 'parameters' => $parameters], $cache->value); } /** * @dataProvider orderDataScopes */ public function test_order_data_scopes_use_the_order_data_repository( string $scope, string $method, array $parameters, ): void { $cache = new class extends UpstreamQueryCache { public mixed $value = null; public function __construct() { } public function refresh(string $scope, array $parameters, Closure $resolver): string { $this->value = $resolver(); return 'fresh'; } }; $caches = new class($cache) extends UpstreamQueryCacheFactory { public function __construct(private readonly UpstreamQueryCache $cache) { } public function forSource(string $source): UpstreamQueryCache { return $this->cache; } }; $information = $this->createMock(UpstreamBusinessInformation::class); $information->expects($this->never())->method('snapshot'); $orders = $this->createMock(OrderDataRepository::class); $orders->expects($this->once()) ->method($method) ->with($parameters['filters'], $parameters['page'], $parameters['limit']) ->willReturn(['rows' => [['id' => 1]]]); $productPrices = $this->createMock(ProductPriceRepository::class); $productPrices->expects($this->never())->method('paginate'); $refresher = new OperationsReplicaSnapshotRefresher($caches, $information, $orders, $productPrices); $this->assertSame('fresh', $refresher->refresh(compact('scope', 'parameters'))); $this->assertSame(['rows' => [['id' => 1]]], $cache->value); } public function test_product_price_scope_uses_the_operations_replica_repository(): void { $cache = new class extends UpstreamQueryCache { public mixed $value = null; public function __construct() { } public function refresh(string $scope, array $parameters, Closure $resolver): string { $this->value = $resolver(); return 'fresh'; } }; $caches = new class($cache) extends UpstreamQueryCacheFactory { public function __construct(private readonly UpstreamQueryCache $cache) { } public function forSource(string $source): UpstreamQueryCache { return $this->cache; } }; $information = $this->createMock(UpstreamBusinessInformation::class); $information->expects($this->never())->method('snapshot'); $orders = $this->createMock(OrderDataRepository::class); $orders->expects($this->never())->method('afterSales'); $orders->expects($this->never())->method('promotionExpenses'); $productPrices = $this->createMock(ProductPriceRepository::class); $filters = [ 'barcode' => 'YMJ005203N1176V01', 'completed_from' => '2025-12-01', 'completed_to' => '2025-12-31', ]; $productPrices->expects($this->once()) ->method('paginate') ->with($filters, 2, 50) ->willReturn(['rows' => [['barcode' => 'YMJ005203N1176V01']]]); $refresher = new OperationsReplicaSnapshotRefresher( $caches, $information, $orders, $productPrices, ); $parameters = ['filters' => $filters, 'page' => 2, 'limit' => 50]; $this->assertSame('fresh', $refresher->refresh([ 'scope' => 'supplier-payables.product-prices.list', 'parameters' => $parameters, ])); $this->assertSame(['rows' => [['barcode' => 'YMJ005203N1176V01']]], $cache->value); } public static function orderDataScopes(): array { $parameters = [ 'filters' => ['date' => '2026-08-09'], 'page' => 1, 'limit' => 25, ]; return [ 'after sales' => ['order-data.after-sales.list', 'afterSales', $parameters], 'promotion' => ['order-data.promotion.list', 'promotionExpenses', $parameters], ]; } public static function snapshotScopes(): array { return [ 'company' => ['company-information.read', ['page' => 2, 'perPage' => 50]], 'store' => ['store-information.read', ['page' => 1, 'perPage' => 25]], 'trademark' => ['trademark-information.read', ['page' => 3, 'perPage' => 100]], ]; } public function test_unknown_business_snapshot_scope_is_rejected_before_connecting_to_an_upstream(): void { /** @var UpstreamBusinessInformation $information */ $information = (new ReflectionClass(UpstreamBusinessInformation::class))->newInstanceWithoutConstructor(); $this->expectException(InvalidArgumentException::class); $information->snapshot('unknown.scope', []); } }