277 lines
11 KiB
PHP
277 lines
11 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Business;
|
|
|
|
use App\Models\Auth\Permission;
|
|
use Modules\EcommerceFinance\Repositories\ProductCatalogMirrorRepository;
|
|
use Tests\Feature\FeatureTestCase;
|
|
|
|
class ProductCatalogPageTest extends FeatureTestCase
|
|
{
|
|
protected function grantAdminPermission(): void
|
|
{
|
|
$this->user->attachPermission(
|
|
Permission::where('name', 'read-admin-panel')->firstOrFail()
|
|
);
|
|
}
|
|
|
|
public function test_normal_products_page_renders_mirror_rows_and_filters(): void
|
|
{
|
|
$this->grantAdminPermission();
|
|
|
|
$state = [
|
|
'status' => 'succeeded',
|
|
'last_succeeded_at' => '2026-08-15 17:51:03',
|
|
'last_full_succeeded_at' => '2026-08-10 03:15:00',
|
|
'mirror_row_count' => 547217,
|
|
];
|
|
$repository = \Mockery::mock(ProductCatalogMirrorRepository::class);
|
|
$repository->shouldReceive('available')->once()->andReturnTrue();
|
|
$repository->shouldReceive('state')->once()->andReturn($state);
|
|
$repository->shouldReceive('syncStatus')->once()->with($state)->andReturn([
|
|
'code' => 'healthy',
|
|
'lag_minutes' => 4,
|
|
]);
|
|
$repository->shouldReceive('ready')->once()->andReturnTrue();
|
|
$repository->shouldReceive('paginate')
|
|
->once()
|
|
->withArgs(static function (array $filters, int $page, int $limit): bool {
|
|
return $filters['i_id'] === 'STYLE-001'
|
|
&& $filters['sku_id'] === 'SKU-001'
|
|
&& $filters['keyword'] === '胡桃'
|
|
&& $filters['enabled'] === '1'
|
|
&& $filters['stock_disabled'] === '0'
|
|
&& $filters['sale_price_min'] === '800'
|
|
&& $filters['sale_price_max'] === '900'
|
|
&& $page === 1
|
|
&& $limit <= 50;
|
|
})
|
|
->andReturn([
|
|
'data' => [(object) [
|
|
'autoid' => 1001,
|
|
'i_id' => 'STYLE-001',
|
|
'sku_id' => 'SKU-001',
|
|
'name' => '岩板餐桌',
|
|
'short_name' => '餐桌',
|
|
'properties_value' => '1.4米',
|
|
'color' => '胡桃色',
|
|
'sale_price' => 899,
|
|
'cost_price' => 500,
|
|
'purchase_price' => 480,
|
|
'product_purchase_price_source' => '商品及库存管理(普通商品资料)',
|
|
'purchase_order_unit_price' => 65,
|
|
'purchase_order_finished_at' => '2026-08-20 11:03:05',
|
|
'purchase_order_id' => 827380,
|
|
'purchase_order_price_source' => '采购单管理-1688代发单价',
|
|
'market_price' => 1099,
|
|
'brand' => '北木',
|
|
'category' => '住宅家具',
|
|
'supplier_name' => '新北供应商',
|
|
'enabled' => 1,
|
|
'stock_disabled' => 0,
|
|
'modified' => '2026-08-15 10:00:00',
|
|
]],
|
|
'total' => 1,
|
|
'purchase_price_sync_status' => [
|
|
'code' => 'ready',
|
|
'initialized' => true,
|
|
'last_succeeded_at' => '2026-08-21 12:00:00',
|
|
],
|
|
]);
|
|
$this->app->instance(ProductCatalogMirrorRepository::class, $repository);
|
|
|
|
$response = $this->loginAs()->get(route('business.pages.show', [
|
|
'section' => 'product-inventory',
|
|
'page' => 'normal-products',
|
|
'i_id' => 'STYLE-001',
|
|
'sku_id' => 'SKU-001',
|
|
'keyword' => '胡桃',
|
|
'enabled' => '1',
|
|
'stock_disabled' => '0',
|
|
'sale_price_min' => '900',
|
|
'sale_price_max' => '800',
|
|
]));
|
|
|
|
$response
|
|
->assertOk()
|
|
->assertSee('data-product-catalog-page', false)
|
|
->assertSeeText('普通商品资料')
|
|
->assertSee('data-sync-state="healthy"', false)
|
|
->assertSeeText('同步正常')
|
|
->assertSeeText('最近同步 2026-08-15 17:51:03')
|
|
->assertSeeText('数据延迟 4 分钟')
|
|
->assertSeeText('最近校准 2026-08-10 03:15:00')
|
|
->assertSeeText('STYLE-001')
|
|
->assertSeeText('SKU-001')
|
|
->assertSeeText('岩板餐桌')
|
|
->assertSeeText('899.00')
|
|
->assertSeeText('普通商品采购价')
|
|
->assertSeeText('1688 代发单价')
|
|
->assertSeeText('商品及库存管理(普通商品资料)')
|
|
->assertSeeText('采购单管理-1688代发单价')
|
|
->assertSeeText('65.00')
|
|
->assertDontSeeText('1688 代发单价尚未完成首次同步')
|
|
->assertSeeText('正常同步')
|
|
->assertDontSee('pic_url');
|
|
}
|
|
|
|
public function test_normal_products_page_marks_purchase_prices_as_not_yet_synchronized(): void
|
|
{
|
|
$this->grantAdminPermission();
|
|
|
|
$state = [
|
|
'status' => 'succeeded',
|
|
'initial_completed_at' => '2026-08-14 21:15:36',
|
|
'last_succeeded_at' => '2026-08-21 12:00:00',
|
|
'mirror_row_count' => 547217,
|
|
];
|
|
$repository = \Mockery::mock(ProductCatalogMirrorRepository::class);
|
|
$repository->shouldReceive('available')->once()->andReturnTrue();
|
|
$repository->shouldReceive('state')->once()->andReturn($state);
|
|
$repository->shouldReceive('syncStatus')->once()->with($state)->andReturn([
|
|
'code' => 'healthy',
|
|
'lag_minutes' => 1,
|
|
]);
|
|
$repository->shouldReceive('ready')->once()->andReturnTrue();
|
|
$repository->shouldReceive('paginate')->once()->andReturn([
|
|
'data' => [],
|
|
'total' => 0,
|
|
'purchase_price_sync_status' => [
|
|
'code' => 'pending',
|
|
'initialized' => false,
|
|
'last_succeeded_at' => null,
|
|
],
|
|
]);
|
|
$this->app->instance(ProductCatalogMirrorRepository::class, $repository);
|
|
|
|
$this->loginAs()->get(route('business.pages.show', [
|
|
'section' => 'product-inventory',
|
|
'page' => 'normal-products',
|
|
]))
|
|
->assertOk()
|
|
->assertSee('data-purchase-price-sync-state="pending"', false)
|
|
->assertSeeText('1688 代发单价尚未完成首次同步')
|
|
->assertSeeText('当前仅显示普通商品采购价');
|
|
}
|
|
|
|
public function test_normal_products_page_does_not_treat_initial_sync_as_empty_data(): void
|
|
{
|
|
$this->grantAdminPermission();
|
|
|
|
$state = ['status' => 'running'];
|
|
$repository = \Mockery::mock(ProductCatalogMirrorRepository::class);
|
|
$repository->shouldReceive('available')->once()->andReturnTrue();
|
|
$repository->shouldReceive('state')->once()->andReturn($state);
|
|
$repository->shouldReceive('syncStatus')->once()->with($state)->andReturn([
|
|
'code' => 'initializing',
|
|
'lag_minutes' => null,
|
|
]);
|
|
$repository->shouldReceive('ready')->once()->andReturnFalse();
|
|
$repository->shouldNotReceive('paginate');
|
|
$this->app->instance(ProductCatalogMirrorRepository::class, $repository);
|
|
|
|
$this->loginAs()->get(route('business.pages.show', [
|
|
'section' => 'product-inventory',
|
|
'page' => 'normal-products',
|
|
]))
|
|
->assertOk()
|
|
->assertSeeText('普通商品资料正在执行首次全量同步')
|
|
->assertSeeText('首次同步中')
|
|
->assertDontSeeText('当前镜像中没有普通商品资料。');
|
|
}
|
|
|
|
public function test_normal_products_page_reports_an_unavailable_mirror(): void
|
|
{
|
|
$this->grantAdminPermission();
|
|
|
|
$repository = \Mockery::mock(ProductCatalogMirrorRepository::class);
|
|
$repository->shouldReceive('available')->once()->andReturnFalse();
|
|
$repository->shouldNotReceive('state');
|
|
$repository->shouldNotReceive('syncStatus');
|
|
$repository->shouldNotReceive('ready');
|
|
$repository->shouldNotReceive('paginate');
|
|
$this->app->instance(ProductCatalogMirrorRepository::class, $repository);
|
|
|
|
$this->loginAs()->get(route('business.pages.show', [
|
|
'section' => 'product-inventory',
|
|
'page' => 'normal-products',
|
|
]))
|
|
->assertOk()
|
|
->assertSeeText('共享业务镜像尚未建立')
|
|
->assertDontSeeText('当前镜像中没有普通商品资料。');
|
|
}
|
|
|
|
public function test_normal_products_page_keeps_old_rows_visible_after_a_sync_failure(): void
|
|
{
|
|
$this->grantAdminPermission();
|
|
|
|
$state = [
|
|
'status' => 'failed',
|
|
'run_mode' => 'incremental',
|
|
'initial_completed_at' => '2026-08-14 21:15:36',
|
|
'last_succeeded_at' => '2026-08-15 17:51:03',
|
|
'mirror_row_count' => 547217,
|
|
];
|
|
$repository = \Mockery::mock(ProductCatalogMirrorRepository::class);
|
|
$repository->shouldReceive('available')->once()->andReturnTrue();
|
|
$repository->shouldReceive('state')->once()->andReturn($state);
|
|
$repository->shouldReceive('syncStatus')->once()->with($state)->andReturn([
|
|
'code' => 'failed',
|
|
'lag_minutes' => 18,
|
|
]);
|
|
$repository->shouldReceive('ready')->once()->andReturnTrue();
|
|
$repository->shouldReceive('paginate')->once()->andReturn([
|
|
'data' => [(object) [
|
|
'autoid' => 1001,
|
|
'i_id' => 'STYLE-OLD',
|
|
'sku_id' => 'SKU-OLD',
|
|
'name' => '最近成功同步的商品',
|
|
'short_name' => null,
|
|
'properties_value' => null,
|
|
'color' => null,
|
|
'sale_price' => 100,
|
|
'cost_price' => null,
|
|
'purchase_price' => null,
|
|
'market_price' => null,
|
|
'brand' => null,
|
|
'category' => null,
|
|
'supplier_name' => null,
|
|
'enabled' => 1,
|
|
'stock_disabled' => 0,
|
|
'modified' => '2026-08-15 17:45:00',
|
|
]],
|
|
'total' => 1,
|
|
]);
|
|
$this->app->instance(ProductCatalogMirrorRepository::class, $repository);
|
|
|
|
$this->loginAs()->get(route('business.pages.show', [
|
|
'section' => 'product-inventory',
|
|
'page' => 'normal-products',
|
|
]))
|
|
->assertOk()
|
|
->assertSee('data-sync-state="failed"', false)
|
|
->assertSeeText('同步失败,显示最近数据')
|
|
->assertSeeText('最近成功同步的商品');
|
|
}
|
|
|
|
public function test_normal_products_page_rejects_users_without_sidebar_permission(): void
|
|
{
|
|
$subject = 'user_' . $this->user->id;
|
|
|
|
setting()->set('system.sidebar_permissions', json_encode([
|
|
'configured' => [$subject],
|
|
'subjects' => [
|
|
$subject => ['company-management/basic-information'],
|
|
],
|
|
], JSON_UNESCAPED_UNICODE));
|
|
|
|
$this->withExceptionHandling()
|
|
->loginAs()
|
|
->get(route('business.pages.show', [
|
|
'section' => 'product-inventory',
|
|
'page' => 'normal-products',
|
|
]))
|
|
->assertForbidden();
|
|
}
|
|
}
|