createMock(UpstreamReadOnlyQuery::class); $bindings = ['%Factory\\%\\_%', '2026-07', '2026-08']; $query->expects($this->once()) ->method('scalar') ->with( $this->callback(function (string $sql): bool { UpstreamReadOnlyQuery::assertReadOnlySql($sql); $this->assertStringContainsString('[dbo].[fys_supplierPaymentSummary]', $sql); $this->assertStringContainsString('[Totalindemnity] <> 0', $sql); return true; }), $bindings, ) ->willReturn(1); $query->expects($this->once()) ->method('select') ->with( $this->callback(function (string $sql): bool { UpstreamReadOnlyQuery::assertReadOnlySql($sql); $this->assertStringContainsString('[Totalindemnity] AS decimal(18,2)', $sql); $this->assertStringContainsString('[remarks] AS remark', $sql); $this->assertStringContainsString('OFFSET 25 ROWS FETCH NEXT 25 ROWS ONLY', $sql); $this->assertStringNotContainsString('fys_paper_factory_compensation_detail', $sql); return true; }), $bindings, ) ->willReturn([[ 'month' => '2026-07', 'supplier' => 'Factory%_', 'amount' => '12.50', 'reason' => 'Reason', 'remark' => 'Remark', ]]); $result = $this->repository($query)->compensationDetails([ 'supplier' => 'Factory%_', 'month_from' => '2026-07', 'month_to' => '2026-08', ], 2, 25); $this->assertSame(1, $result['total']); $this->assertSame(12.5, $result['data'][0]['amount']); $this->assertSame('Remark', $result['data'][0]['remark']); } public function test_compensation_summary_and_options_are_bounded_read_only_queries(): void { $query = $this->createMock(UpstreamReadOnlyQuery::class); $query->expects($this->once()) ->method('first') ->with( $this->callback(function (string $sql): bool { UpstreamReadOnlyQuery::assertReadOnlySql($sql); $this->assertStringContainsString('SUM([Totalindemnity])', $sql); return true; }), [], ) ->willReturn(['total_amount' => '20.25', 'total_count' => '2']); $query->expects($this->once()) ->method('select') ->with($this->callback(function (string $sql): bool { UpstreamReadOnlyQuery::assertReadOnlySql($sql); $this->assertStringContainsString('DISTINCT TOP 200', $sql); return true; })) ->willReturn([['value' => 'Supplier A']]); $repository = $this->repository($query); $this->assertSame( ['totalAmount' => 20.25, 'totalCount' => 2], $repository->compensationDetailSummary([]), ); $this->assertSame( ['suppliers' => ['Supplier A']], $repository->filterOptionsForCompensation(), ); } private function repository(UpstreamReadOnlyQuery $query): SupplierPayableRepository { $factory = $this->createMock(UpstreamReadOnlyQueryFactory::class); $factory->expects($this->once()) ->method('forSource') ->with('jiansun') ->willReturn($query); return new SupplierPayableRepository($factory); } }