CaiWuXiTong/tests/Unit/EcommerceFinance/ThirdPartyWarehouseStorageUpstreamRepositoryTest.php

197 lines
9.0 KiB
PHP

<?php
namespace Tests\Unit\EcommerceFinance;
use Modules\EcommerceFinance\Repositories\ThirdPartyWarehouseStorageUpstreamRepository;
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQuery;
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQueryFactory;
use PHPUnit\Framework\TestCase;
class ThirdPartyWarehouseStorageUpstreamRepositoryTest extends TestCase
{
public function test_header_queries_cover_all_warehouses_with_a_bounded_id_window(): void
{
$query = new RecordingThirdPartyWarehouseQuery();
$query->scalarResult = 987654;
$repository = $this->repository($query);
$this->assertSame(987654, $repository->headerHighWatermark());
$repository->headerRange(100, 200);
$this->assertStringContainsString('MAX([Id])', $query->scalarCalls[0]['sql']);
$this->assertStringContainsString('[dbo].[jst_inoutWater]', $query->scalarCalls[0]['sql']);
$this->assertStringNotContainsString('[wms_co_id] = ?', $query->scalarCalls[0]['sql']);
$this->assertSame([], $query->scalarCalls[0]['bindings']);
$this->assertStringContainsString('[Id] > ?', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('[Id] <= ?', $query->selectCalls[0]['sql']);
$this->assertStringNotContainsString('[wms_co_id] = ?', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('h.[wh_id]', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('[io_date]', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('AS occurred_at', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('NOT EXISTS', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('newer.[io_id] = h.[io_id]', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('newer.[Id] > h.[Id]', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('ORDER BY h.[Id]', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('h.[io_date] >= ?', $query->selectCalls[0]['sql']);
$this->assertSame([100, 200, '2026-07-01'], $query->selectCalls[0]['bindings']);
}
public function test_items_are_parameterized_and_split_into_bounded_io_id_batches(): void
{
$query = new RecordingThirdPartyWarehouseQuery();
$query->selectResults = [[
['source_id' => 2, 'io_id' => 2],
], [
['source_id' => 1, 'io_id' => 501],
]];
$repository = $this->repository($query);
$rows = $repository->itemsForIoIds(range(1, 501));
$this->assertCount(2, $query->selectCalls);
$this->assertCount(500, $query->selectCalls[0]['bindings']);
$this->assertCount(1, $query->selectCalls[1]['bindings']);
$this->assertStringContainsString('[dbo].[jst_inoutWaterItem]', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('[qty_type]', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('[io_id] IN (', $query->selectCalls[0]['sql']);
$this->assertStringNotContainsString('[io_id] IN (1,2', $query->selectCalls[0]['sql']);
$this->assertSame([1, 2], array_column($rows, 'source_id'));
}
public function test_incremental_headers_use_a_bounded_modified_id_cursor_without_a_warehouse_filter(): void
{
$query = new RecordingThirdPartyWarehouseQuery();
$repository = $this->repository($query);
$repository->modifiedHeadersBatch(
'2026-09-09 03:20:00.000',
123,
'2026-09-11 03:20:00.000',
99999,
);
$call = $query->selectCalls[0];
$this->assertStringContainsString('SELECT TOP (5000)', $call['sql']);
$this->assertStringContainsString('[modified] >= ?', $call['sql']);
$this->assertStringContainsString('[modified] < ?', $call['sql']);
$this->assertStringContainsString('[modified] = ? AND [Id] > ?', $call['sql']);
$this->assertStringContainsString('ORDER BY [modified], [Id]', $call['sql']);
$this->assertStringNotContainsString('[wms_co_id] = ?', $call['sql']);
$this->assertSame([
'2026-09-09 03:20:00.000',
'2026-09-11 03:20:00.000',
'2026-09-09 03:20:00.000',
'2026-09-09 03:20:00.000',
123,
], $call['bindings']);
}
public function test_changed_documents_reload_only_the_latest_header_in_bounded_batches(): void
{
$query = new RecordingThirdPartyWarehouseQuery();
$query->selectResults = [[
['source_id' => 2, 'io_id' => 2],
], [
['source_id' => 1, 'io_id' => 501],
]];
$repository = $this->repository($query);
$rows = $repository->latestHeadersForIoIds(range(1, 501), '2026-09-11 03:20:00.000');
$this->assertCount(2, $query->selectCalls);
$this->assertCount(502, $query->selectCalls[0]['bindings']);
$this->assertCount(3, $query->selectCalls[1]['bindings']);
$this->assertStringContainsString('MAX([Id]) AS latest_id', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('[io_id] IN (', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('[modified] < ?', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('ON latest.[latest_id] = h.[Id] WHERE h.[io_date] >= ?', $query->selectCalls[0]['sql']);
$this->assertStringNotContainsString('[modified] < ? AND [io_date] >= ?', $query->selectCalls[0]['sql']);
$this->assertStringNotContainsString('[wms_co_id] = ?', $query->selectCalls[0]['sql']);
$this->assertSame('2026-09-11 03:20:00.000', $query->selectCalls[0]['bindings'][500]);
$this->assertSame('2026-07-01', $query->selectCalls[0]['bindings'][501]);
$this->assertSame([1, 2], array_column($rows, 'source_id'));
$repository->latestHeadersForIoIds([], '2026-09-11 03:20:00.000');
$this->assertCount(2, $query->selectCalls);
}
public function test_inventory_uses_all_warehouse_keyset_pagination_and_a_bounded_limit(): void
{
$query = new RecordingThirdPartyWarehouseQuery();
$query->scalarResult = 191818;
$repository = $this->repository($query);
$this->assertSame(191818, $repository->inventoryHighWatermark());
$repository->inventoryBatch(12350, 191818, 99999);
$this->assertStringContainsString('[dbo].[jst_inventoryInfo]', $query->scalarCalls[0]['sql']);
$this->assertStringNotContainsString('[wms_co_id] = ?', $query->scalarCalls[0]['sql']);
$this->assertSame([], $query->scalarCalls[0]['bindings']);
$this->assertStringContainsString('SELECT TOP (5000)', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('[dbo].[jst_inventoryInfo]', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('[Id] > ?', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('[Id] <= ?', $query->selectCalls[0]['sql']);
$this->assertStringNotContainsString('[wms_co_id] = ?', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('ORDER BY [Id]', $query->selectCalls[0]['sql']);
$this->assertStringContainsString('AS source_modified_at', $query->selectCalls[0]['sql']);
foreach (['return_qty', 'in_qty', 'defective_qty', 'customize_qty_1', 'customize_qty_2', 'customize_qty_3'] as $column) {
$this->assertStringContainsString("[{$column}]", $query->selectCalls[0]['sql']);
}
$this->assertSame([12350, 191818], $query->selectCalls[0]['bindings']);
$repository->inventoryBatch(191818, 191818, 2000);
$this->assertCount(1, $query->selectCalls);
}
private function repository(UpstreamReadOnlyQuery $query): ThirdPartyWarehouseStorageUpstreamRepository
{
$factory = new class($query) extends UpstreamReadOnlyQueryFactory {
public function __construct(private readonly UpstreamReadOnlyQuery $query)
{
}
public function forSource(string $source): UpstreamReadOnlyQuery
{
TestCase::assertSame('jiansun', $source);
return $this->query;
}
};
return new ThirdPartyWarehouseStorageUpstreamRepository($factory);
}
}
class RecordingThirdPartyWarehouseQuery extends UpstreamReadOnlyQuery
{
public mixed $scalarResult = 0;
/** @var list<list<array<string,mixed>>> */
public array $selectResults = [];
/** @var list<array{sql:string,bindings:array}> */
public array $selectCalls = [];
/** @var list<array{sql:string,bindings:array}> */
public array $scalarCalls = [];
public function __construct()
{
parent::__construct('jiansun');
}
public function select(string $sql, array $bindings = []): array
{
$this->selectCalls[] = compact('sql', 'bindings');
return array_shift($this->selectResults) ?? [];
}
public function scalar(string $sql, array $bindings = []): mixed
{
$this->scalarCalls[] = compact('sql', 'bindings');
return $this->scalarResult;
}
}