223 lines
7.8 KiB
PHP
223 lines
7.8 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit\EcommerceFinance;
|
|
|
|
use App\Business\UpstreamBusinessInformation;
|
|
use Closure;
|
|
use InvalidArgumentException;
|
|
use Modules\EcommerceFinance\Repositories\OrderDataRepository;
|
|
use Modules\EcommerceFinance\Repositories\ProductPriceRepository;
|
|
use Modules\EcommerceFinance\Services\OperationsReplicaSnapshotRefresher;
|
|
use Modules\EcommerceFinance\Services\UpstreamQueryCache;
|
|
use Modules\EcommerceFinance\Services\UpstreamQueryCacheFactory;
|
|
use PHPUnit\Framework\TestCase;
|
|
use ReflectionClass;
|
|
|
|
class OperationsReplicaSnapshotRefresherTest extends TestCase
|
|
{
|
|
/**
|
|
* @dataProvider snapshotScopes
|
|
*/
|
|
public function test_background_refresh_uses_the_same_fixed_business_snapshot_query(
|
|
string $scope,
|
|
array $parameters,
|
|
): void {
|
|
$cache = new class extends UpstreamQueryCache {
|
|
public array $calls = [];
|
|
|
|
public mixed $value = null;
|
|
|
|
public function __construct()
|
|
{
|
|
}
|
|
|
|
public function refresh(string $scope, array $parameters, Closure $resolver): string
|
|
{
|
|
$this->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', []);
|
|
}
|
|
}
|