service(function (string $platform, string $start, string $end) use (&$seenPeriods): array { $seenPeriods[] = [$platform, $start, $end]; $index = array_search($platform, array_column(StoreReceivingBalanceOverviewService::PLATFORMS, 'slug'), true) + 1; return [ 'opening_balance' => 100.10 * $index, 'income' => 10.20 * $index, 'expense' => 1.25 * $index, ]; }); $result = $service->overview('2026-10'); $this->assertSame('2026-10', $result['month']); $this->assertSame(['start' => '2026-10-01', 'end' => '2026-10-31'], $result['period']); $this->assertCount(8, $result['platforms']); $this->assertCount(8, $seenPeriods); $this->assertSame('taobao-alipay', $seenPeriods[0][0]); $this->assertSame('xiaohongshu', $seenPeriods[7][0]); $this->assertSame(3603.60, $result['summary']['opening_balance']); $this->assertSame(367.20, $result['summary']['income']); $this->assertSame(45.00, $result['summary']['expense']); $this->assertSame(3925.80, $result['summary']['balance']); $this->assertTrue($result['summary']['complete']); $this->assertSame(8, $result['summary']['available_platform_count']); } public function test_overview_does_not_report_a_total_when_an_opening_balance_is_missing(): void { $service = $this->service(function (string $platform): array { return [ 'opening_balance' => $platform === 'vipshop-jx' ? null : 25.0, 'income' => 5.0, 'expense' => 2.0, ]; }); $result = $service->overview('2026-09'); $this->assertFalse($result['summary']['complete']); $this->assertNull($result['summary']['opening_balance']); $this->assertNull($result['summary']['income']); $this->assertNull($result['summary']['expense']); $this->assertNull($result['summary']['balance']); $this->assertSame(7, $result['summary']['available_platform_count']); $vipshopJx = array_values(array_filter( $result['platforms'], static fn (array $platform): bool => $platform['slug'] === 'vipshop-jx', ))[0]; $this->assertFalse($vipshopJx['available']); } private function service(callable $summary): StoreReceivingBalanceOverviewService { return new class($summary) extends StoreReceivingBalanceOverviewService { public function __construct(private $summaryCallback) { } protected function fetchPlatformSummary(string $platform, string $start, string $end): array { return ($this->summaryCallback)($platform, $start, $end); } }; } }