user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail()); $repository = \Mockery::mock(CourierFreightReconciliationMirrorRepository::class); $repository->shouldReceive('available')->once()->andReturnTrue(); $repository->shouldReceive('ready')->once()->andReturnTrue(); $repository->shouldReceive('paginate')->once()->andReturn([ 'data' => [[ 'waybill' => '00123456789012345678', 'product_id' => 'ITEM-1', 'send_time' => '2026-09-01 12:00:00', 'transaction_order_no' => 'SO-1', 'province' => '江苏省', 'city' => '常州市', 'district' => '武进区', 'barcode' => 'SKU-1', 'express_name' => '测试快递', 'express_name_2' => '测试网点', 'quantity' => 2, 'product_name' => '验收商品', 'platform' => '测试平台', 'shop_name' => '验收店铺', 'warehouse' => '验收仓库', 'volume' => 7161, 'supplier' => '验收供应商', 'length' => 11, 'width' => 21, 'height' => 31, 'freight' => 8.8, 'reconciliation_weight' => 0.7, 'reconciliation_volume' => 7161, ]], 'total' => $summaryReady ? 1 : null, 'has_more' => false, 'summary' => $summaryReady ? ['count' => 1, 'freight' => 8.8] : null, 'sync' => ['code' => 'fresh', 'last_succeeded_at' => '2026-09-09 10:00:00', 'lag_minutes' => 0], ]); $this->app->instance(CourierFreightReconciliationMirrorRepository::class, $repository); return $this->loginAs()->get(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'freight-reconciliation', ])); } public function test_rendered_values_match_their_column_headings(): void { $response = $this->renderFreightPage()->assertOk(); $document = new \DOMDocument(); $previous = libxml_use_internal_errors(true); $document->loadHTML('' . $response->getContent()); libxml_clear_errors(); libxml_use_internal_errors($previous); $xpath = new \DOMXPath($document); $headers = $xpath->query('//table[contains(@class,"recon-table")]/thead/tr/th'); $values = $xpath->query('//table[contains(@class,"recon-table")]/tbody/tr[1]/td'); $this->assertCount(23, $headers); $this->assertCount(23, $values); $row = []; foreach ($headers as $index => $header) $row[trim($header->textContent)] = trim($values->item($index)->textContent); $this->assertSame('7161', $row['体积(cm3)']); $this->assertSame('验收供应商', $row['供应商']); $this->assertSame('11', $row['长(cm)']); $this->assertSame('21', $row['宽(cm)']); $this->assertSame('31', $row['高(cm)']); $this->assertSame('8.80', $row['运费']); $this->assertSame('00123456789012345678', $row['快递单号']); } public function test_rows_remain_available_while_the_complete_summary_is_pending(): void { $this->renderFreightPage(false)->assertOk() ->assertSeeText('验收商品') ->assertSeeText('正在计算完整筛选汇总') ->assertSee('data-summary-ready="false"', false) ->assertSee('data-summary-count>—', false); } public function test_summary_request_keeps_filters_and_returns_only_the_summary(): void { $this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail()); $repository = \Mockery::mock(CourierFreightReconciliationMirrorRepository::class); $repository->shouldReceive('requestSummary')->once() ->withArgs(fn (array $filters) => $filters['month'] === '2026-09' && $filters['express_no'] === 'YT') ->andReturn(['count' => 48, 'freight' => 8.8, 'computed_at' => '2026-09-09 10:00:00']); $this->app->instance(CourierFreightReconciliationMirrorRepository::class, $repository); $this->loginAs()->get(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'freight-reconciliation', 'courier_summary' => 1, 'month' => '2026-09', 'express_no' => ' YT ', ]))->assertOk()->assertJsonPath('data.count', 48)->assertJsonPath('data.freight', 8.8); } public function test_busy_summary_returns_pending_without_starting_another_calculation(): void { $this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail()); $repository = \Mockery::mock(CourierFreightReconciliationMirrorRepository::class); $repository->shouldReceive('requestSummary')->once()->andReturnNull(); $this->app->instance(CourierFreightReconciliationMirrorRepository::class, $repository); $this->loginAs()->get(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'freight-reconciliation', 'courier_summary' => 1, ]))->assertStatus(202)->assertJsonPath('data', null)->assertHeader('Content-Length', '13')->assertHeader('X-Accel-Buffering', 'no'); } public function test_summary_is_not_available_without_login(): void { $repository = \Mockery::mock(CourierFreightReconciliationMirrorRepository::class); $repository->shouldNotReceive('requestSummary'); $this->app->instance(CourierFreightReconciliationMirrorRepository::class, $repository); $this->withExceptionHandling()->get(route('business.pages.show', [ 'company_id' => $this->company->id, 'section' => 'courier-payables', 'page' => 'freight-reconciliation', 'courier_summary' => 1, ]))->assertRedirect(); } }