ui = $this->getMockBuilder(CronUIInterface::class)->disableOriginalConstructor()->getMock(); $this->logger = $this->getMockBuilder(LoggerInterface::class)->disableOriginalConstructor()->getMock(); $this->entityManager = $this->getMockBuilder(EntityManagerInterface::class)->disableOriginalConstructor()->getMock(); $this->connection = $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock(); $this->entityManager->method('getConnection')->willReturn($this->connection); } /** * Sets up common connection expectations. */ private function setupConnectionExpectations(): void { $this->connection->method('connect'); $this->connection->method('isConnected')->willReturn(true); $this->connection->method('getParams')->willReturn([ 'driver' => 'mysql', 'host' => 'localhost', 'port' => '3306', 'dbname' => 'testdb', 'user' => 'testuser', ]); } /** * Creates and sets up a schema manager mock. */ private function setupSchemaManager(): AbstractSchemaManager|MockObject { $schemaManager = $this->getMockBuilder(AbstractSchemaManager::class)->disableOriginalConstructor()->getMock(); $this->connection->method('createSchemaManager')->willReturn($schemaManager); return $schemaManager; } private function getMockFor(string $method): MockObject|DbCheck { $dbCheck = $this->getMockBuilder(DbCheck::class) ->setConstructorArgs([$this->entityManager]) ->setMethodsExcept([$method, 'setLoggerInterface', 'setUI']) ->getMock(); $dbCheck->setUI($this->ui); $dbCheck->setLoggerInterface($this->logger); return $dbCheck; } private function getMockWithConnection(string $method): MockObject|DbCheck { // Create a mock that allows us to override the private getDbConnection method $dbCheck = $this->getMockBuilder(DbCheck::class) ->setConstructorArgs([$this->entityManager]) ->onlyMethods(['getDbConnection']) ->getMock(); // Mock the getDbConnection method to return our connection $dbCheck->method('getDbConnection')->willReturn($this->connection); $dbCheck->setUI($this->ui); $dbCheck->setLoggerInterface($this->logger); return $dbCheck; } public function testPreview(): void { $dbCheck = $this->getMockFor('preview'); $this->ui->expects(self::once()) ->method('print') ->with('No preview available for this job'); $dbCheck->preview(); } public function testExecuteWithSuccessfulConnection(): void { $dbCheck = $this->getMockFor('execute'); $schemaManager = $this->setupSchemaManager(); $this->setupConnectionExpectations(); $tables = ['table1', 'table2', 'messenger_messages']; $schemaManager->method('listTableNames')->willReturn($tables); $result1 = $this->getMockBuilder(Result::class)->disableOriginalConstructor()->getMock(); $result1->method('fetchOne')->willReturn(10); $result2 = $this->getMockBuilder(Result::class)->disableOriginalConstructor()->getMock(); $result2->method('fetchOne')->willReturn(20); $this->connection->expects(self::exactly(2)) ->method('executeQuery') ->willReturnCallback(function ($query) use ($result1, $result2) { if (strpos($query, 'table1') !== false) { return $result1; } elseif (strpos($query, 'table2') !== false) { return $result2; } return $this->getMockBuilder(Result::class)->disableOriginalConstructor()->getMock(); }); $this->logger->expects(self::atLeastOnce())->method('info'); $this->logger->expects(self::atLeastOnce())->method('debug'); $this->logger->expects(self::never())->method('error'); $this->logger->expects(self::never())->method('critical'); $dbCheck->execute(); } public function testExecuteWithEmptyTables(): void { $dbCheck = $this->getMockFor('execute'); $schemaManager = $this->setupSchemaManager(); $this->setupConnectionExpectations(); // Include both regular tables and tables that should be ignored $tables = ['table1', 'table2', 'messenger_messages', 'enqueue', 'tag_control']; $schemaManager->method('listTableNames')->willReturn($tables); $emptyResult = $this->getMockBuilder(Result::class)->disableOriginalConstructor()->getMock(); $emptyResult->method('fetchOne')->willReturn(0); // We expect exactly 2 queries (for table1 and table2) // The other tables (messenger_messages, enqueue, tag_control) should be ignored $this->connection ->expects(self::exactly(2)) ->method('executeQuery') ->willReturnCallback(function ($query) use ($emptyResult) { // Verify that only non-ignored tables are queried if (strpos($query, 'messenger_messages') !== false || strpos($query, 'enqueue') !== false || strpos($query, 'tag_control') !== false) { $this->fail('Ignored table should not be queried: '.$query); } return $emptyResult; }); // Verify debug logs for ignored tables $this->logger->expects(self::atLeastOnce()) ->method('debug') ->with(self::logicalOr( self::stringContains('Nombre total de tables: 5'), self::stringContains('Table messenger_messages: -- ignored --'), self::stringContains('Table enqueue: -- ignored --'), self::stringContains('Table tag_control: -- ignored --'), self::stringContains('Table table1: 0 enregistrements'), self::stringContains('Table table2: 0 enregistrements'), self::anything() // Allow other debug messages )); // Verify critical log for empty tables detection $this->logger->expects(self::atLeastOnce()) ->method('critical') ->with(self::stringContains('tables vides détectées')); // Verify error logs for empty tables list $this->logger->expects(self::atLeastOnce()) ->method('error') ->withConsecutive( [self::identicalTo('Tables vides:')], [self::stringContains('- table1')], [self::stringContains('- table2')] ); $this->logger->expects(self::atLeastOnce())->method('info'); $dbCheck->execute(); } /** * Test that the DbCheck class handles connection errors properly. */ public function testConnectionError(): void { $dbCheck = $this->getMockFor('execute'); $this->connection ->method('connect') ->willThrowException(new \RuntimeException('Connection error')); $this->entityManager ->method('getConnection') ->willReturn($this->connection); $this->logger->expects(self::atLeastOnce())->method('info'); $this->logger->expects(self::atLeastOnce())->method('critical'); $this->expectException(\Error::class); $dbCheck->execute(); } public function testExecuteWithSchemaError(): void { $dbCheck = $this->getMockFor('execute'); $schemaManager = $this->setupSchemaManager(); $this->setupConnectionExpectations(); // Setup schema manager to throw exception $schemaManager ->method('listTableNames') ->willThrowException(new \Exception('Schema error')); $this->logger->expects(self::atLeastOnce())->method('info'); $this->logger->expects(self::once())->method('critical') ->with('Erreur lors de la vérification de l\'intégrité des données: Schema error'); $dbCheck->execute(); } public function testExecuteWithTableQueryException(): void { $dbCheck = $this->getMockFor('execute'); $schemaManager = $this->setupSchemaManager(); $this->setupConnectionExpectations(); $tables = ['table1', 'table2', 'messenger_messages']; $schemaManager->method('listTableNames')->willReturn($tables); // Make executeQuery throw an exception for table1 but return normal results for table2 $result2 = $this->getMockBuilder(Result::class)->disableOriginalConstructor()->getMock(); $result2->method('fetchOne')->willReturn(20); $this->connection->expects(self::exactly(2)) ->method('executeQuery') ->willReturnCallback(function ($query) use ($result2) { if (strpos($query, 'table1') !== false) { throw new \Exception('Error executing query on table1'); } elseif (strpos($query, 'table2') !== false) { return $result2; } return $this->getMockBuilder(Result::class)->disableOriginalConstructor()->getMock(); }); // We expect a critical log for the table1 error $this->logger->expects(self::atLeastOnce())->method('critical') ->withConsecutive( [self::anything()], ['Impossible de vérifier la table table1: Error executing query on table1'] ); $this->logger->expects(self::atLeastOnce())->method('info'); $this->logger->expects(self::atLeastOnce())->method('debug'); $dbCheck->execute(); } }