timeFactory = $this->createMock(ITimeFactory::class); $this->connection = $this->createMock(IDBConnection::class); $this->logger = $this->createMock(LoggerInterface::class); $this->jobList = $this->createMock(IJobList::class); $this->job = new CleanupOrphanedChildrenJob( $this->timeFactory, $this->connection, $this->logger, $this->jobList, ); } private function getArgument(): array { return [ 'childTable' => 'childTable', 'parentTable' => 'parentTable', 'parentId' => 'parentId', 'logMessage' => 'logMessage', ]; } private function getMockQueryBuilder(): IQueryBuilder&MockObject { $expr = $this->createMock(IExpressionBuilder::class); $qb = $this->createMock(IQueryBuilder::class); $qb->method('select') ->willReturnSelf(); $qb->method('from') ->willReturnSelf(); $qb->method('leftJoin') ->willReturnSelf(); $qb->method('where') ->willReturnSelf(); $qb->method('setMaxResults') ->willReturnSelf(); $qb->method('andWhere') ->willReturnSelf(); $qb->method('expr') ->willReturn($expr); $qb->method('delete') ->willReturnSelf(); return $qb; } public function testRunWithoutOrphans(): void { $argument = $this->getArgument(); $selectQb = $this->getMockQueryBuilder(); $result = $this->createMock(IResult::class); $this->connection->expects(self::once()) ->method('getQueryBuilder') ->willReturn($selectQb); $selectQb->expects(self::once()) ->method('executeQuery') ->willReturn($result); $result->expects(self::once()) ->method('fetchAll') ->willReturn([]); $result->expects(self::once()) ->method('closeCursor'); $this->jobList->expects(self::never()) ->method('add'); self::invokePrivate($this->job, 'run', [$argument]); } public function testRunWithPartialBatch(): void { $argument = $this->getArgument(); $selectQb = $this->getMockQueryBuilder(); $deleteQb = $this->getMockQueryBuilder(); $result = $this->createMock(IResult::class); $calls = [ $selectQb, $deleteQb, ]; $this->connection->method('getQueryBuilder') ->willReturnCallback(function () use (&$calls) { return array_shift($calls); }); $selectQb->expects(self::once()) ->method('executeQuery') ->willReturn($result); $result->expects(self::once()) ->method('fetchAll') ->willReturn([ ['id' => 42], ['id' => 43], ]); $result->expects(self::once()) ->method('closeCursor'); $deleteQb->expects(self::once()) ->method('delete') ->willReturnSelf(); $deleteQb->expects(self::once()) ->method('executeStatement'); $this->jobList->expects(self::never()) ->method('add'); self::invokePrivate($this->job, 'run', [$argument]); } public function testRunWithFullBatch(): void { $argument = $this->getArgument(); $selectQb = $this->getMockQueryBuilder(); $deleteQb = $this->getMockQueryBuilder(); $result = $this->createMock(IResult::class); $calls = [ $selectQb, $deleteQb, ]; $this->connection->method('getQueryBuilder') ->willReturnCallback(function () use (&$calls) { return array_shift($calls); }); $selectQb->expects(self::once()) ->method('executeQuery') ->willReturn($result); $result->expects(self::once()) ->method('fetchAll') ->willReturn(array_map(static fn ($i) => ['id' => 42 + $i], range(0, 999))); $result->expects(self::once()) ->method('closeCursor'); $deleteQb->expects(self::once()) ->method('delete') ->willReturnSelf(); $deleteQb->expects(self::once()) ->method('executeStatement'); $this->jobList->expects(self::once()) ->method('add') ->with(CleanupOrphanedChildrenJob::class, $argument); self::invokePrivate($this->job, 'run', [$argument]); } }