CaiWuXiTong/tests/Feature/Business/NonJingxiRefundPageTest.php

325 lines
14 KiB
PHP

<?php
namespace Tests\Feature\Business;
use App\Models\Auth\Permission;
use Modules\EcommerceFinance\Repositories\NonJingxiRefundMirrorRepository;
use Tests\Feature\FeatureTestCase;
class NonJingxiRefundPageTest extends FeatureTestCase
{
protected function grantAdminPermission(): void
{
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail()
);
}
public function test_page_renders_mirror_rows_second_page_and_filters(): void
{
$this->grantAdminPermission();
setting()->set('default.list_limit', 25);
$states = [
NonJingxiRefundMirrorRepository::INFO_DATASET => ['status' => 'running', 'initial_completed_at' => '2026-08-15 00:00:00'],
NonJingxiRefundMirrorRepository::ITEM_DATASET => ['status' => 'running', 'initial_completed_at' => '2026-08-15 00:00:00'],
];
$repository = \Mockery::mock(NonJingxiRefundMirrorRepository::class);
$repository->shouldReceive('available')->once()->andReturnTrue();
$repository->shouldReceive('states')->once()->andReturn($states);
$repository->shouldReceive('syncStatus')->once()->with($states)->andReturn([
'code' => 'syncing',
'last_succeeded_at' => '2026-08-21 12:00:00',
]);
$repository->shouldReceive('ready')->once()->andReturnTrue();
$repository->shouldReceive('queryReady')->once()->andReturnTrue();
$repository->shouldReceive('paginate')->once()->withArgs(
static fn (array $filters, int $page, int $limit): bool =>
$filters === [
'from' => '2026-08-01',
'to' => '2026-08-21',
'order_no' => 'ORDER-1',
'shop_name' => '测试店',
'sku_id' => 'SKU-1',
] && $page === 2 && $limit === 25
)->andReturn([
'data' => [(object) [
'Id' => 102,
'refundDate' => '2026-08-20 10:00:00',
'as_id' => 2002,
'outer_as_id' => 'AS-2002',
'o_id' => 3002,
'so_id' => 'ORDER-1',
'type' => '退款',
'node' => '完成',
'isZC' => '是',
'shop_name' => '测试店',
'platform' => '天猫',
'responsibleDepartment' => '电商部',
'totalRefundMoney' => 99.5,
'originPostcode' => 'ZIP',
'shop_i_id' => 'ITEM-1',
'shop_sku_id' => 'SHOP-SKU-1',
'i_id' => 'STYLE-1',
'sku_id' => 'SKU-1',
'buyer_paid_amount' => 120,
'qty' => 1,
'unitCostGoods' => 50,
'detailRefundMoney' => 99.5,
'refundType' => '退货退款',
'refundQty' => 1,
'refundCostGoods' => 50,
'RefundMoneyIncome' => 99.5,
'AfSalesMoney' => 99.5,
'createTime' => '2026-08-20 09:00:00',
'status' => '完成',
'shop_status' => '完成',
'created' => '2026-08-20 09:00:00',
'confirm_date' => '2026-08-20 10:00:00',
]],
'total' => 26,
]);
$this->app->instance(NonJingxiRefundMirrorRepository::class, $repository);
$url = route('business.pages.show', [
'company_id' => $this->company->id,
'section' => 'refunds',
'page' => 'non-jingxi-refund-data',
]) . '?' . http_build_query([
'from' => '2026-08-01', 'to' => '2026-08-21', 'order_no' => 'ORDER-1',
'shop_name' => '测试店', 'sku_id' => 'SKU-1', 'page' => 2,
]);
$this->loginAs()->get($url)
->assertOk()
->assertSee('data-filter-contract="non-jingxi-refund-data.filters"', false)
->assertSee('data-sync-state="syncing"', false)
->assertSeeText('业务镜像同步中,当前显示最近成功数据')
->assertSeeText('AS-2002')
->assertSeeText('SKU-1')
->assertSeeText('99.50')
->assertSeeText('共 26 条记录')
->assertSeeText('第 2 / 2 页')
->assertSeeText('导出筛选结果')
->assertSee('workspace/refunds/non-jingxi-refund-data/export', false)
->assertSee('value="2026-08-01"', false)
->assertSee('value="ORDER-1"', false);
}
public function test_refund_export_downloads_filtered_csv(): void
{
$this->grantAdminPermission();
$repository = \Mockery::mock(NonJingxiRefundMirrorRepository::class);
$repository->shouldReceive('available')->once()->andReturnTrue();
$repository->shouldReceive('ready')->once()->andReturnTrue();
$repository->shouldReceive('queryReady')->once()->andReturnTrue();
$repository->shouldReceive('exportRows')->once()->with([
'from' => '2026-08-01',
'to' => '2026-08-21',
'order_no' => 'ORDER-1',
'shop_name' => '测试店',
'sku_id' => 'SKU-1',
])->andReturn([(object) [
'Id' => 102,
'refundDate' => '2026-08-20 10:00:00',
'as_id' => 2002,
'outer_as_id' => 'AS-2002',
'o_id' => 3002,
'so_id' => 'ORDER-1',
'type' => '退款',
'node' => '完成',
'isZC' => '是',
'shop_name' => '测试店',
'platform' => '天猫',
'responsibleDepartment' => '电商部',
'totalRefundMoney' => 99.5,
'originPostcode' => 'ZIP',
'shop_i_id' => 'ITEM-1',
'shop_sku_id' => 'SHOP-SKU-1',
'i_id' => 'STYLE-1',
'sku_id' => 'SKU-1',
'buyer_paid_amount' => 120,
'qty' => 1,
'unitCostGoods' => 50,
'detailRefundMoney' => 99.5,
'refundType' => '退货退款',
'refundQty' => 1,
'refundCostGoods' => 50,
'RefundMoneyIncome' => 99.5,
'AfSalesMoney' => 99.5,
'createTime' => '2026-08-20 09:00:00',
'status' => '完成',
'shop_status' => '完成',
'created' => '2026-08-20 09:00:00',
'confirm_date' => '2026-08-20 10:00:00',
]]);
$this->app->instance(NonJingxiRefundMirrorRepository::class, $repository);
$response = $this->loginAs()->get(route('business.refunds.non-jingxi-refund-data.export', [
'from' => '2026-08-01',
'to' => '2026-08-21',
'order_no' => 'ORDER-1',
'shop_name' => '测试店',
'sku_id' => 'SKU-1',
]));
$response->assertOk()->assertDownload('after_sales_refunds_2026-08-01_2026-08-21.csv');
$content = $response->streamedContent();
$this->assertStringStartsWith("\xEF\xBB\xBF", $content);
$this->assertStringContainsString('序号/主键ID', $content);
$this->assertStringContainsString('AS-2002', $content);
$this->assertStringContainsString('99.5', $content);
}
public function test_page_does_not_report_empty_data_before_initial_sync_finishes(): void
{
$this->grantAdminPermission();
$states = [
NonJingxiRefundMirrorRepository::INFO_DATASET => ['status' => 'running'],
NonJingxiRefundMirrorRepository::ITEM_DATASET => ['status' => 'running'],
];
$repository = \Mockery::mock(NonJingxiRefundMirrorRepository::class);
$repository->shouldReceive('available')->once()->andReturnTrue();
$repository->shouldReceive('states')->once()->andReturn($states);
$repository->shouldReceive('syncStatus')->once()->with($states)->andReturn([
'code' => 'initializing',
'last_succeeded_at' => null,
]);
$repository->shouldReceive('ready')->once()->andReturnFalse();
$repository->shouldNotReceive('queryReady');
$repository->shouldNotReceive('paginate');
$this->app->instance(NonJingxiRefundMirrorRepository::class, $repository);
$this->loginAs()->get(route('business.pages.show', [
'section' => 'refunds',
'page' => 'non-jingxi-refund-data',
]))
->assertOk()
->assertSeeText('业务镜像正在执行首次同步')
->assertSeeText('首次同步尚未完成,完成后将自动显示数据。')
->assertDontSeeText('当前筛选范围暂无数据。');
}
public function test_page_shows_a_real_empty_filter_result(): void
{
$this->grantAdminPermission();
$states = [
NonJingxiRefundMirrorRepository::INFO_DATASET => ['status' => 'succeeded', 'initial_completed_at' => '2026-08-15 00:00:00'],
NonJingxiRefundMirrorRepository::ITEM_DATASET => ['status' => 'succeeded', 'initial_completed_at' => '2026-08-15 00:00:00'],
];
$repository = \Mockery::mock(NonJingxiRefundMirrorRepository::class);
$repository->shouldReceive('available')->once()->andReturnTrue();
$repository->shouldReceive('states')->once()->andReturn($states);
$repository->shouldReceive('syncStatus')->once()->andReturn(['code' => 'healthy', 'last_succeeded_at' => '2026-08-21 12:00:00']);
$repository->shouldReceive('ready')->once()->andReturnTrue();
$repository->shouldReceive('queryReady')->once()->andReturnTrue();
$repository->shouldReceive('paginate')->once()->andReturn(['data' => [], 'total' => 0]);
$this->app->instance(NonJingxiRefundMirrorRepository::class, $repository);
$this->loginAs()->get(route('business.pages.show', [
'section' => 'refunds',
'page' => 'non-jingxi-refund-data',
'sku_id' => 'NOT-FOUND',
]))
->assertOk()
->assertSeeText('当前筛选范围暂无数据。')
->assertSeeText('共 0 条记录')
->assertSee('is-disabled', false)
->assertSeeText('导出筛选结果');
}
public function test_page_does_not_run_the_query_before_required_indexes_exist(): void
{
$this->grantAdminPermission();
$states = [
NonJingxiRefundMirrorRepository::INFO_DATASET => ['status' => 'running', 'initial_completed_at' => '2026-08-15 00:00:00'],
NonJingxiRefundMirrorRepository::ITEM_DATASET => ['status' => 'running', 'initial_completed_at' => '2026-08-15 00:00:00'],
];
$repository = \Mockery::mock(NonJingxiRefundMirrorRepository::class);
$repository->shouldReceive('available')->once()->andReturnTrue();
$repository->shouldReceive('states')->once()->andReturn($states);
$repository->shouldReceive('syncStatus')->once()->with($states)->andReturn([
'code' => 'syncing',
'last_succeeded_at' => '2026-08-17 10:05:07',
]);
$repository->shouldReceive('ready')->once()->andReturnTrue();
$repository->shouldReceive('queryReady')->once()->andReturnFalse();
$repository->shouldNotReceive('paginate');
$this->app->instance(NonJingxiRefundMirrorRepository::class, $repository);
$this->loginAs();
$url = route('business.pages.show', [
'section' => 'refunds',
'page' => 'non-jingxi-refund-data',
]).'?page=2';
$this->get($url)
->assertOk()
->assertSee('upstream-data-alert--warning', false)
->assertSeeText('页面查询索引正在准备,生产同步轮转完成后将自动显示数据。')
->assertSeeText('页面查询索引尚未就绪,当前不会执行高压力查询。')
->assertSeeText('第 1 / 1 页')
->assertDontSeeText('第 2 / 1 页')
->assertDontSeeText('当前筛选范围暂无数据。');
}
public function test_page_replaces_invalid_dates_with_the_default_range(): void
{
$this->grantAdminPermission();
$this->travelTo('2026-08-21 12:00:00');
$states = [
NonJingxiRefundMirrorRepository::INFO_DATASET => ['status' => 'succeeded', 'initial_completed_at' => '2026-08-15 00:00:00'],
NonJingxiRefundMirrorRepository::ITEM_DATASET => ['status' => 'succeeded', 'initial_completed_at' => '2026-08-15 00:00:00'],
];
$repository = \Mockery::mock(NonJingxiRefundMirrorRepository::class);
$repository->shouldReceive('available')->once()->andReturnTrue();
$repository->shouldReceive('states')->once()->andReturn($states);
$repository->shouldReceive('syncStatus')->once()->with($states)->andReturn([
'code' => 'healthy',
'last_succeeded_at' => '2026-08-21 12:00:00',
]);
$repository->shouldReceive('ready')->once()->andReturnTrue();
$repository->shouldReceive('queryReady')->once()->andReturnTrue();
$repository->shouldReceive('paginate')->once()->withArgs(
static fn (array $filters, int $page, int $limit): bool =>
$filters['from'] === '2026-08-15'
&& $filters['to'] === '2026-08-21'
&& $page === 1
&& $limit === 25
)->andReturn(['data' => [], 'total' => 0]);
$this->app->instance(NonJingxiRefundMirrorRepository::class, $repository);
$this->loginAs()->get(route('business.pages.show', [
'section' => 'refunds',
'page' => 'non-jingxi-refund-data',
'from' => '2026-02-31',
'to' => 'not-a-date',
]))
->assertOk()
->assertSee('value="2026-08-15"', false)
->assertSee('value="2026-08-21"', false)
->assertDontSee('非京喜退款数据暂时无法读取,请稍后重试。');
}
public function test_page_rejects_users_without_sidebar_permission(): void
{
$subject = 'user_' . $this->user->id;
$this->user->roles()->sync([\App\Models\Auth\Role::where('name', 'admin')->firstOrFail()->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' => 'refunds',
'page' => 'non-jingxi-refund-data',
]))->assertForbidden();
}
}