diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f8eecf0..73bb0cb5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,6 +60,22 @@ jobs: run: | ./vendor/bin/phpstan --error-format=github + tests-unit: + name: Run Unit tests + runs-on: ubuntu-latest + container: joomlaprojects/docker-images:php${{ matrix.php_version }} + needs: [code-style-php] + strategy: + matrix: + php_version: ['8.3', '8.4', '8.5'] + steps: + - uses: actions/checkout@v6 + - name: Run Unit tests + run: | + git config --global --add safe.directory $GITHUB_WORKSPACE + composer update + ./vendor/bin/phpunit + tests-unit-sqlite: name: Run Unit tests on SQLite runs-on: ubuntu-latest diff --git a/Tests/Command/ExportCommandTest.php b/Tests/Command/ExportCommandTest.php index 93a48bba..358139d0 100644 --- a/Tests/Command/ExportCommandTest.php +++ b/Tests/Command/ExportCommandTest.php @@ -10,17 +10,17 @@ use Joomla\Console\Application; use Joomla\Database\Command\ExportCommand; use Joomla\Database\DatabaseDriver; -use Joomla\Database\Exception\ExecutionFailureException; +use Joomla\Database\DatabaseExporter; use Joomla\Database\Exception\UnsupportedAdapterException; use Joomla\Filesystem\Folder; -use Joomla\Test\DatabaseTestCase; +use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\BufferedOutput; /** * Test class for Joomla\Database\Command\ExportCommand */ -class ExportCommandTest extends DatabaseTestCase +class ExportCommandTest extends TestCase { /** * Path to the test space @@ -48,10 +48,6 @@ public static function setUpBeforeClass(): void } parent::setUpBeforeClass(); - - if (!static::$connection || static::$connection->getName() !== 'mysql') { - self::markTestSkipped('MySQL database not configured.'); - } } /** @@ -64,20 +60,6 @@ protected function setUp(): void { parent::setUp(); - try { - foreach (DatabaseDriver::splitSql(file_get_contents(dirname(__DIR__) . '/Stubs/Schema/mysql.sql')) as $query) { - static::$connection->setQuery($query) - ->execute(); - } - } catch (ExecutionFailureException $exception) { - $this->markTestSkipped( - \sprintf( - 'Could not load MySQL database: %s', - $exception->getMessage() - ) - ); - } - $this->umask = umask(0); $this->testPath = sys_get_temp_dir() . '/' . microtime(true) . '.' . mt_rand(); @@ -98,55 +80,35 @@ protected function tearDown(): void umask($this->umask); - foreach (static::$connection->getTableList() as $table) { - static::$connection->dropTable($table); - } - parent::tearDown(); } - /** - * Loads the example data into the database. - * - * @return void - */ - protected function loadExampleData(): void - { - $data = [ - (object) [ - 'id' => 1, - 'title' => 'Testing1', - 'start_date' => '2019-10-26 00:00:00', - 'description' => 'test row one', - ], - (object) [ - 'id' => 2, - 'title' => 'Testing2', - 'start_date' => '2019-10-26 00:00:00', - 'description' => 'test row two', - ], - (object) [ - 'id' => 3, - 'title' => 'Testing3', - 'start_date' => '2019-10-26 00:00:00', - 'description' => 'test row three', - ], - (object) [ - 'id' => 4, - 'title' => 'Testing4', - 'start_date' => '2019-10-26 00:00:00', - 'description' => 'test row four', - ], - ]; - - foreach ($data as $row) { - static::$connection->insertObject('#__dbtest', $row); - } - } - public function testTheDatabaseIsExportedWithAllTables() { - $this->loadExampleData(); + $db = $this->createMock(DatabaseDriver::class); + $exporter = $this->createMock(DatabaseExporter::class); + + $exporter->expects($this->once()) + ->method('withStructure') + ->with(true) + ->willReturnSelf(); + $exporter->expects($this->exactly(2)) + ->method('withData') + ->with(true) + ->willReturnSelf(); + $exporter->expects($this->exactly(2)) + ->method('from') + ->willReturnSelf(); + + $db->expects($this->once()) + ->method('getExporter') + ->willReturn($exporter); + $db->expects($this->once()) + ->method('getTableList') + ->willReturn(['dbtest', 'dbtest2']); + $db->expects($this->once()) + ->method('getPrefix') + ->willReturn(''); $input = new ArrayInput( [ @@ -158,18 +120,47 @@ public function testTheDatabaseIsExportedWithAllTables() $application = new Application($input, $output); - $command = new ExportCommand(static::$connection); + $command = new ExportCommand($db); $command->setApplication($application); $this->assertSame(0, $command->execute($input, $output)); + $this->assertTrue(is_file($this->testPath . '/dbtest.xml')); + $this->assertTrue(is_file($this->testPath . '/dbtest2.xml')); $screenOutput = $output->fetch(); + $this->assertStringContainsString('Processing the dbtest table', $screenOutput); + $this->assertStringContainsString('Exported data for dbtest in', $screenOutput); + $this->assertStringContainsString('Processing the dbtest2 table', $screenOutput); + $this->assertStringContainsString('Exported data for dbtest2 in', $screenOutput); $this->assertStringContainsString('Export completed in', $screenOutput); } public function testTheDatabaseIsExportedWithAllTablesInZipFormat() { - $this->loadExampleData(); + $db = $this->createMock(DatabaseDriver::class); + $exporter = $this->createMock(DatabaseExporter::class); + + $exporter->expects($this->once()) + ->method('withStructure') + ->with(true) + ->willReturnSelf(); + $exporter->expects($this->exactly(2)) + ->method('withData') + ->with(true) + ->willReturnSelf(); + $exporter->expects($this->exactly(2)) + ->method('from') + ->willReturnSelf(); + + $db->expects($this->once()) + ->method('getExporter') + ->willReturn($exporter); + $db->expects($this->once()) + ->method('getTableList') + ->willReturn(['dbtest', 'dbtest2']); + $db->expects($this->once()) + ->method('getPrefix') + ->willReturn(''); $input = new ArrayInput( [ @@ -182,18 +173,46 @@ public function testTheDatabaseIsExportedWithAllTablesInZipFormat() $application = new Application($input, $output); - $command = new ExportCommand(static::$connection); + $command = new ExportCommand($db); $command->setApplication($application); $this->assertSame(0, $command->execute($input, $output)); + $this->assertCount(1, glob($this->testPath . '/data_exported_*.zip')); $screenOutput = $output->fetch(); + $this->assertStringContainsString('Processing the dbtest table', $screenOutput); + $this->assertStringContainsString('Exported data for dbtest in', $screenOutput); + $this->assertStringContainsString('Processing the dbtest2 table', $screenOutput); + $this->assertStringContainsString('Exported data for dbtest2 in', $screenOutput); $this->assertStringContainsString('Export completed in', $screenOutput); } public function testTheDatabaseIsExportedWithASingleTable() { - $this->loadExampleData(); + $db = $this->createMock(DatabaseDriver::class); + $exporter = $this->createMock(DatabaseExporter::class); + + $exporter->expects($this->once()) + ->method('withStructure') + ->with(true) + ->willReturnSelf(); + $exporter->expects($this->once()) + ->method('withData') + ->with(true) + ->willReturnSelf(); + $exporter->expects($this->once()) + ->method('from') + ->willReturnSelf(); + + $db->expects($this->once()) + ->method('getExporter') + ->willReturn($exporter); + $db->expects($this->once()) + ->method('getTableList') + ->willReturn(['dbtest', 'dbtest2']); + $db->expects($this->once()) + ->method('getPrefix') + ->willReturn(''); $input = new ArrayInput( [ @@ -206,12 +225,16 @@ public function testTheDatabaseIsExportedWithASingleTable() $application = new Application($input, $output); - $command = new ExportCommand(static::$connection); + $command = new ExportCommand($db); $command->setApplication($application); $this->assertSame(0, $command->execute($input, $output)); + $this->assertTrue(is_file($this->testPath . '/dbtest.xml')); + $this->assertFalse(is_file($this->testPath . '/dbtest2.xml')); $screenOutput = $output->fetch(); + $this->assertStringContainsString('Processing the dbtest table', $screenOutput); + $this->assertStringContainsString('Exported data for dbtest in', $screenOutput); $this->assertStringContainsString('Export completed in', $screenOutput); } @@ -247,7 +270,29 @@ public function testTheCommandFailsIfTheDatabaseDriverDoesNotSupportExports() public function testTheCommandFailsIfTheRequestedTableDoesNotExistInTheDatabase() { - $this->loadExampleData(); + $db = $this->createMock(DatabaseDriver::class); + $exporter = $this->createMock(DatabaseExporter::class); + + $exporter->expects($this->once()) + ->method('withStructure') + ->with(true) + ->willReturnSelf(); + $exporter->expects($this->never()) + ->method('withData') + ->willReturnSelf(); + $exporter->expects($this->never()) + ->method('from') + ->willReturnSelf(); + + $db->expects($this->once()) + ->method('getExporter') + ->willReturn($exporter); + $db->expects($this->once()) + ->method('getTableList') + ->willReturn(['dbtest']); + $db->expects($this->once()) + ->method('getPrefix') + ->willReturn(''); $input = new ArrayInput( [ @@ -260,7 +305,7 @@ public function testTheCommandFailsIfTheRequestedTableDoesNotExistInTheDatabase( $application = new Application($input, $output); - $command = new ExportCommand(static::$connection); + $command = new ExportCommand($db); $command->setApplication($application); $this->assertSame(1, $command->execute($input, $output)); diff --git a/Tests/Command/ImportCommandTest.php b/Tests/Command/ImportCommandTest.php index 109a5cf0..97135f95 100644 --- a/Tests/Command/ImportCommandTest.php +++ b/Tests/Command/ImportCommandTest.php @@ -10,15 +10,16 @@ use Joomla\Console\Application; use Joomla\Database\Command\ImportCommand; use Joomla\Database\DatabaseDriver; +use Joomla\Database\DatabaseImporter; use Joomla\Database\Exception\UnsupportedAdapterException; -use Joomla\Test\DatabaseTestCase; +use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\BufferedOutput; /** * Test class for Joomla\Database\Command\ImportCommand */ -class ImportCommandTest extends DatabaseTestCase +class ImportCommandTest extends TestCase { /** * Path to the database stubs @@ -39,10 +40,6 @@ public static function setUpBeforeClass(): void } parent::setUpBeforeClass(); - - if (!static::$connection || static::$connection->getName() !== 'mysql') { - self::markTestSkipped('MySQL database not configured.'); - } } /** @@ -58,23 +55,30 @@ protected function setUp(): void $this->stubPath = dirname(__DIR__) . '/Stubs/Importer'; } - /** - * Tears down the fixture, for example, close a network connection. - * This method is called after a test is executed. - * - * @return void - */ - protected function tearDown(): void + public function testTheDatabaseIsImportedWithAllTables() { - foreach (static::$connection->getTableList() as $table) { - static::$connection->dropTable($table); - } + $db = $this->createMock(DatabaseDriver::class); + $importer = $this->createMock(DatabaseImporter::class); + + $importer->expects($this->once()) + ->method('withStructure') + ->with(true) + ->willReturnSelf(); + $importer->expects($this->once()) + ->method('asXml') + ->willReturnSelf(); + $importer->expects($this->once()) + ->method('mergeStructure'); + $importer->expects($this->once()) + ->method('importData'); - parent::tearDown(); - } + $db->expects($this->once()) + ->method('getImporter') + ->willReturn($importer); + $db->expects($this->once()) + ->method('dropTable') + ->with('dbtest', true); - public function testTheDatabaseIsImportedWithAllTables() - { $input = new ArrayInput( [ 'command' => 'database:import', @@ -85,17 +89,42 @@ public function testTheDatabaseIsImportedWithAllTables() $application = new Application($input, $output); - $command = new ImportCommand(static::$connection); + $command = new ImportCommand($db); $command->setApplication($application); $this->assertSame(0, $command->execute($input, $output)); $screenOutput = $output->fetch(); + $this->assertStringContainsString('Importing dbtest from dbtest.xml', $screenOutput); + $this->assertStringContainsString('Processing the dbtest table', $screenOutput); + $this->assertStringContainsString('Imported data for dbtest.xml in', $screenOutput); $this->assertStringContainsString('Import completed in', $screenOutput); } public function testTheDatabaseIsImportedWithASingleTable() { + $db = $this->createMock(DatabaseDriver::class); + $importer = $this->createMock(DatabaseImporter::class); + + $importer->expects($this->once()) + ->method('withStructure') + ->with(true) + ->willReturnSelf(); + $importer->expects($this->once()) + ->method('asXml') + ->willReturnSelf(); + $importer->expects($this->once()) + ->method('mergeStructure'); + $importer->expects($this->once()) + ->method('importData'); + + $db->expects($this->once()) + ->method('getImporter') + ->willReturn($importer); + $db->expects($this->once()) + ->method('dropTable') + ->with('dbtest', true); + $input = new ArrayInput( [ 'command' => 'database:import', @@ -107,12 +136,15 @@ public function testTheDatabaseIsImportedWithASingleTable() $application = new Application($input, $output); - $command = new ImportCommand(static::$connection); + $command = new ImportCommand($db); $command->setApplication($application); $this->assertSame(0, $command->execute($input, $output)); $screenOutput = $output->fetch(); + $this->assertStringContainsString('Importing dbtest from dbtest.xml', $screenOutput); + $this->assertStringContainsString('Processing the dbtest table', $screenOutput); + $this->assertStringContainsString('Imported data for dbtest.xml in', $screenOutput); $this->assertStringContainsString('Import completed in', $screenOutput); } @@ -148,6 +180,27 @@ public function testTheCommandFailsIfTheDatabaseDriverDoesNotSupportImports() public function testTheCommandFailsIfTheRequestedTableDoesNotHaveAnImportFile() { + $db = $this->createMock(DatabaseDriver::class); + $importer = $this->createMock(DatabaseImporter::class); + + $importer->expects($this->once()) + ->method('withStructure') + ->with(true) + ->willReturnSelf(); + $importer->expects($this->once()) + ->method('asXml') + ->willReturnSelf(); + $importer->expects($this->never()) + ->method('mergeStructure'); + $importer->expects($this->never()) + ->method('importData'); + + $db->expects($this->once()) + ->method('getImporter') + ->willReturn($importer); + $db->expects($this->never()) + ->method('dropTable'); + $input = new ArrayInput( [ 'command' => 'database:import', @@ -159,7 +212,7 @@ public function testTheCommandFailsIfTheRequestedTableDoesNotHaveAnImportFile() $application = new Application($input, $output); - $command = new ImportCommand(static::$connection); + $command = new ImportCommand($db); $command->setApplication($application); $this->assertSame(1, $command->execute($input, $output)); diff --git a/phpunit.xml.dist b/phpunit.xml.dist index a4fd6ec2..7f59d1b6 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,12 +1,12 @@ - - - - + + + + - + @@ -17,7 +17,6 @@ Tests/Monitor Tests/Query Tests/Service - Tests/AbstractDatabaseDriverTestCase.php Tests/DatabaseAwareTraitTest.php Tests/DatabaseExporterTest.php Tests/DatabaseFactoryTest.php