createMock(\medoo::class); $queries = []; $mockDb->method('query') ->willReturnCallback(function ($sql, $params = []) use (&$queries) { $queries[] = ['sql' => $sql, 'params' => $params]; if (strpos($sql, 'COUNT(0)') !== false) { return new class { public function fetchAll() { return [[1]]; } }; } return new class { public function fetchAll() { return [[ 'client_id' => 5, 'client_name' => 'Jan', 'client_surname' => 'Kowalski', 'client_email' => 'jan@example.com', 'client_phone' => '123', 'client_city' => 'Warszawa', 'total_orders' => 3, 'total_spent' => 199.99, 'is_registered' => 1, ]]; } }; }); $repository = new ClientRepository($mockDb); $result = $repository->listForAdmin( [], 'client_name DESC; DROP TABLE pp_shop_orders; --', 'DESC; DELETE FROM pp_users; --', 1, 999 ); $this->assertCount(2, $queries); $dataSql = $queries[1]['sql']; $this->assertMatchesRegularExpression('/ORDER BY\s+c\.client_surname\s+ASC,\s+c\.client_surname\s+ASC,\s+c\.client_name\s+ASC/i', $dataSql); $this->assertStringNotContainsString('DROP TABLE', $dataSql); $this->assertMatchesRegularExpression('/LIMIT\s+100\s+OFFSET\s+0/i', $dataSql); $this->assertSame(1, $result['total']); $this->assertCount(1, $result['items']); $this->assertSame('Jan', $result['items'][0]['client_name']); } public function testOrdersForClientReturnsEmptyOnMissingInput(): void { $mockDb = $this->createMock(\medoo::class); $mockDb->expects($this->never())->method('query'); $repository = new ClientRepository($mockDb); $this->assertSame([], $repository->ordersForClient('', 'Kowalski', 'jan@example.com')); $this->assertSame([], $repository->ordersForClient('Jan', '', 'jan@example.com')); $this->assertSame([], $repository->ordersForClient('Jan', 'Kowalski', '')); } public function testOrdersForClientNormalizesRows(): void { $mockDb = $this->createMock(\medoo::class); $mockDb->expects($this->once()) ->method('query') ->willReturn(new class { public function fetchAll() { return [[ 'id' => '10', 'date_order' => '2026-02-15 10:00:00', 'summary' => '149.50', 'payment_method' => 'Przelew', 'transport' => 'Kurier', 'message' => null, ]]; } }); $repository = new ClientRepository($mockDb); $rows = $repository->ordersForClient('Jan', 'Kowalski', 'jan@example.com'); $this->assertCount(1, $rows); $this->assertSame(10, $rows[0]['id']); $this->assertSame(149.50, $rows[0]['summary']); $this->assertSame('Przelew', $rows[0]['payment_method']); $this->assertSame('', $rows[0]['message']); } public function testTotalsForClientReturnsZeroForMissingInput(): void { $mockDb = $this->createMock(\medoo::class); $mockDb->expects($this->never())->method('query'); $repository = new ClientRepository($mockDb); $totals = $repository->totalsForClient('', 'Kowalski', 'jan@example.com'); $this->assertSame(0, $totals['total_orders']); $this->assertSame(0.0, $totals['total_spent']); } public function testTotalsForClientReturnsAggregatedValues(): void { $mockDb = $this->createMock(\medoo::class); $mockDb->expects($this->once()) ->method('query') ->willReturn(new class { public function fetchAll() { return [[ 'total_orders' => '4', 'total_spent' => '456.78', ]]; } }); $repository = new ClientRepository($mockDb); $totals = $repository->totalsForClient('Jan', 'Kowalski', 'jan@example.com'); $this->assertSame(4, $totals['total_orders']); $this->assertSame(456.78, $totals['total_spent']); } }