diff --git a/.env b/.env index da52e373..a27200d4 100644 --- a/.env +++ b/.env @@ -1,5 +1,4 @@ DATABASE_URL="mysql://root:1234@127.0.0.1:3307/foundry_test?serverVersion=5.7.42" MONGO_URL="mongodb://127.0.0.1:27018/dbName?compressors=disabled&gssapiServiceName=mongodb" -DATABASE_RESET_MODE="schema" USE_DAMA_DOCTRINE_TEST_BUNDLE="0" PHPUNIT_VERSION="9" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d5bced4..3bc50982 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ on: jobs: tests: - name: P:${{ matrix.php }}, S:${{ matrix.symfony }}, D:${{ matrix.database }}, PU:${{ matrix.phpunit }}${{ matrix.deps == 'lowest' && ' (lowest)' || '' }}${{ matrix.use-dama == 1 && contains(matrix.database, 'sql') && ' (dama)' || '' }}${{ !contains(matrix.database, 'sql') && '' || matrix.use-migrate == 1 && ' (migrate)' || ' (schema)' }} + name: P:${{ matrix.php }}, S:${{ matrix.symfony }}, D:${{ matrix.database }}, PU:${{ matrix.phpunit }}${{ matrix.deps == 'lowest' && ' (lowest)' || '' }}${{ matrix.use-dama == 1 && contains(matrix.database, 'sql') && ' (dama)' || '' }} runs-on: ubuntu-latest strategy: fail-fast: false @@ -18,7 +18,6 @@ jobs: symfony: [ 6.4.*, 7.0.*, 7.1.* ] database: [ mysql, mongo ] use-dama: [ 1 ] - use-migrate: [ 0 ] phpunit: [ 9 ] exclude: - php: 8.1 @@ -31,70 +30,60 @@ jobs: symfony: '*' database: none use-dama: 1 - use-migrate: 0 phpunit: 9 - php: 8.3 deps: highest symfony: '*' database: mysql|mongo use-dama: 1 - use-migrate: 0 phpunit: 9 - php: 8.3 deps: highest symfony: '*' database: pgsql|mongo use-dama: 1 - use-migrate: 0 phpunit: 9 - php: 8.3 deps: highest symfony: '*' database: pgsql use-dama: 0 - use-migrate: 0 phpunit: 9 - php: 8.3 deps: highest symfony: '*' database: sqlite use-dama: 0 - use-migrate: 0 phpunit: 9 - php: 8.3 deps: lowest symfony: '*' database: sqlite use-dama: 0 - use-migrate: 0 phpunit: 9 - php: 8.3 deps: lowest symfony: '*' database: mysql use-dama: 1 - use-migrate: 0 phpunit: 9 - php: 8.3 deps: highest symfony: '*' database: mysql use-dama: 1 - use-migrate: 1 phpunit: 9 - php: 8.3 deps: highest symfony: '*' database: mysql|mongo use-dama: 1 - use-migrate: 0 phpunit: 10 - php: 8.3 deps: highest symfony: '*' database: mysql|mongo use-dama: 1 - use-migrate: 0 phpunit: 11 env: DATABASE_URL: ${{ contains(matrix.database, 'mysql') && 'mysql://root:root@localhost:3306/foundry?serverVersion=5.7.42' || contains(matrix.database, 'pgsql') && 'postgresql://root:root@localhost:5432/foundry?serverVersion=15' || contains(matrix.database, 'sqlite') && 'sqlite:///%kernel.project_dir%/var/data.db' || '' }} @@ -145,8 +134,68 @@ jobs: - name: Test run: ./phpunit shell: bash + + test-reset-database-with-migration: + name: Test migration - D:${{ matrix.database }}, ${{ matrix.use-dama == 1 && ' (dama)' || '' }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + database: [ mysql, pgsql, sqlite ] + use-dama: [ 0, 1 ] + with-migration-configuration-file: + - '' + - 'tests/Fixture/MigrationTests/configs/migration-configuration.php' + - 'tests/Fixture/MigrationTests/configs/migration-configuration-transactional.php' + exclude: + - database: mysql + with-migration-configuration-file: 'tests/Fixture/MigrationTests/configs/migration-configuration-transactional.php' + env: + DATABASE_URL: ${{ contains(matrix.database, 'mysql') && 'mysql://root:root@localhost:3306/foundry?serverVersion=5.7.42' || contains(matrix.database, 'pgsql') && 'postgresql://root:root@localhost:5432/foundry?serverVersion=15' || 'sqlite:///%kernel.project_dir%/var/data.db' }} + MONGO_URL: '' + USE_DAMA_DOCTRINE_TEST_BUNDLE: ${{ matrix.use-dama == 1 && 1 || 0 }} + WITH_MIGRATION_CONFIGURATION_FILE: ${{ matrix.with-migration-configuration-file }} + PHPUNIT_VERSION: 9 + services: + postgres: + image: ${{ contains(matrix.database, 'pgsql') && 'postgres:15' || '' }} + env: + POSTGRES_USER: root + POSTGRES_PASSWORD: root + POSTGRES_DB: foundry + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + coverage: none + tools: flex + + - name: Install dependencies + uses: ramsey/composer-install@v2 + with: + dependency-versions: highest + composer-options: --prefer-dist env: - DATABASE_RESET_MODE: ${{ matrix.use-migrate == 1 && 'migrate' || 'schema' }} + SYMFONY_REQUIRE: 7.1.* + + - name: Set up MySQL + if: contains(matrix.database, 'mysql') + run: sudo /etc/init.d/mysql start + + - name: Test + run: ./phpunit --testsuite migrate --bootstrap tests/bootstrap-migrate.php + shell: bash code-coverage: name: Code Coverage diff --git a/.gitignore b/.gitignore index ee9e61fd..6d5aba94 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,5 @@ /docker/.makefile /.env.local /docker-compose.override.yaml -/tests/Fixture/Migrations/ +/tests/Fixture/MigrationTests/Migrations/ /tests/Fixture/Maker/tmp/ diff --git a/README.md b/README.md index 4472e279..ba954bc3 100644 --- a/README.md +++ b/README.md @@ -47,20 +47,15 @@ $ docker compose up --detach # install dependencies $ composer update -# run test suite with all available permutations -$ composer test - -# run only one permutation +# run main testsuite (with "schema" reset database strategy) +$ composer test-schema +# or $ ./phpunit -# run test suite with dama/doctrine-test-bundle -$ USE_DAMA_DOCTRINE_TEST_BUNDLE=1 vendor/bin/phpunit - -# run test suite with postgreSQL instead of MySQL -$ DATABASE_URL="postgresql://zenstruck:zenstruck@127.0.0.1:5433/zenstruck_foundry?serverVersion=15" vendor/bin/phpunit - -# run test suite with another PHPUnit version -$ PHPUNIT_VERSION=10 vendor/bin/phpunit +# run "migrate" testsuite (with "migrate" reset database strategy) +$ composer test-migrate +# or +$ ./phpunit --testsuite migrate --bootstrap tests/bootstrap-migrate.php ``` ### Overriding the default configuration @@ -69,11 +64,20 @@ You can override default environment variables by creating a `.env.local` file, ```bash # .env.local -DATABASE_URL="postgresql://zenstruck:zenstruck@127.0.0.1:5433/zenstruck_foundry?serverVersion=15" # enables postgreSQL instead of MySQL + +# change the database to postgreSQL... +DATABASE_URL="postgresql://zenstruck:zenstruck@127.0.0.1:5433/zenstruck_foundry?serverVersion=15" +# ...or to SQLite +DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db" + MONGO_URL="" # disables Mongo USE_DAMA_DOCTRINE_TEST_BUNDLE="1" # enables dama/doctrine-test-bundle PHPUNIT_VERSION="11" # possible values: 9, 10, 11, 11.4 +# test reset database with configuration migration, +# only relevant for "migrate" testsuite +WITH_MIGRATION_CONFIGURATION_FILE="tests/Fixture/MigrationTests/configs/migration-configuration.php" + # run test suite with postgreSQL $ vendor/bin/phpunit ``` diff --git a/composer.json b/composer.json index aec2e99e..3a6b8ad5 100644 --- a/composer.json +++ b/composer.json @@ -48,7 +48,12 @@ "Zenstruck\\Foundry\\": "src/", "Zenstruck\\Foundry\\Psalm\\": "utils/psalm" }, - "files": ["src/functions.php", "src/Persistence/functions.php", "src/phpunit_helper.php"] + "files": [ + "src/functions.php", + "src/Persistence/functions.php", + "src/phpunit_helper.php", + "src/symfony_console.php" + ] }, "autoload-dev": { "psr-4": { @@ -77,22 +82,15 @@ }, "scripts": { "test": [ - "@test-schema-no-dama", - "@test-migrate-no-dama", - "@test-schema-dama", - "@test-migrate-dama" + "@test-schema", + "@test-migrate" ], - "test-schema-no-dama": "DATABASE_RESET_MODE=schema USE_DAMA_DOCTRINE_TEST_BUNDLE=0 ./phpunit", - "test-migrate-no-dama": "DATABASE_RESET_MODE=migrate USE_DAMA_DOCTRINE_TEST_BUNDLE=0 ./phpunit", - "test-schema-dama": "DATABASE_RESET_MODE=schema USE_DAMA_DOCTRINE_TEST_BUNDLE=1 ./phpunit", - "test-migrate-dama": "DATABASE_RESET_MODE=migrate USE_DAMA_DOCTRINE_TEST_BUNDLE=1 ./phpunit" + "test-schema": "./phpunit", + "test-migrate": "./phpunit --testsuite migrate --bootstrap tests/bootstrap-migrate.php" }, "scripts-descriptions": { - "test": "Run all test permutations", - "test-schema-no-dama": "Test with schema reset (no dama/doctrine-test-bundle)", - "test-migrate-no-dama": "Test with migrations reset (no dama/doctrine-test-bundle)", - "test-schema-dama": "Test with schema reset and dama/doctrine-test-bundle", - "test-migrate-dama": "Test with migrations reset and dama/doctrine-test-bundle" + "test-schema": "Test with schema reset", + "test-migrate": "Test with migrations reset" }, "minimum-stability": "dev", "prefer-stable": true diff --git a/phpunit-10.xml.dist b/phpunit-10.xml.dist index 1727f9b8..5af371de 100644 --- a/phpunit-10.xml.dist +++ b/phpunit-10.xml.dist @@ -14,8 +14,12 @@ - + tests + tests/Integration/Migration/ResetDatabaseWithMigrationTest.php + + + tests/Integration/Migration/ResetDatabaseWithMigrationTest.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 6aee915f..dcad3f88 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -7,6 +7,7 @@ colors="true" failOnRisky="true" failOnWarning="true" + defaultTestSuite="main" > @@ -16,8 +17,12 @@ - + tests + tests/Integration/Migration/ResetDatabaseWithMigrationTest.php + + + tests/Integration/Migration/ResetDatabaseWithMigrationTest.php diff --git a/src/Mongo/MongoSchemaResetter.php b/src/Mongo/MongoSchemaResetter.php index 5cf45384..83d39e8a 100644 --- a/src/Mongo/MongoSchemaResetter.php +++ b/src/Mongo/MongoSchemaResetter.php @@ -5,7 +5,9 @@ namespace Zenstruck\Foundry\Mongo; use Symfony\Component\HttpKernel\KernelInterface; -use Zenstruck\Foundry\Persistence\SymfonyCommandRunner; + +use function Zenstruck\Foundry\application; +use function Zenstruck\Foundry\runCommand; /** * @internal @@ -13,8 +15,6 @@ */ final class MongoSchemaResetter implements MongoResetter { - use SymfonyCommandRunner; - /** * @param list $managers */ @@ -24,15 +24,15 @@ public function __construct(private array $managers) public function resetBeforeEachTest(KernelInterface $kernel): void { - $application = self::application($kernel); + $application = application($kernel); foreach ($this->managers as $manager) { try { - self::runCommand($application, 'doctrine:mongodb:schema:drop', ['--dm' => $manager]); + runCommand($application, "doctrine:mongodb:schema:drop --dm={$manager}"); } catch (\Exception) { } - self::runCommand($application, 'doctrine:mongodb:schema:create', ['--dm' => $manager]); + runCommand($application, "doctrine:mongodb:schema:create --dm={$manager}"); } } } diff --git a/src/ORM/ResetDatabase/BaseOrmResetter.php b/src/ORM/ResetDatabase/BaseOrmResetter.php index 4044fe28..4da35f67 100644 --- a/src/ORM/ResetDatabase/BaseOrmResetter.php +++ b/src/ORM/ResetDatabase/BaseOrmResetter.php @@ -9,7 +9,10 @@ use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Platforms\SQLitePlatform; use Symfony\Bundle\FrameworkBundle\Console\Application; -use Zenstruck\Foundry\Persistence\SymfonyCommandRunner; + +use Symfony\Component\Filesystem\Filesystem; + +use function Zenstruck\Foundry\runCommand; /** * @author Nicolas PHILIPPE @@ -17,8 +20,6 @@ */ abstract class BaseOrmResetter { - use SymfonyCommandRunner; - /** * @param list $managers * @param list $connections @@ -39,29 +40,24 @@ final protected function dropAndResetDatabase(Application $application): void if ($databasePlatform instanceof SQLitePlatform) { // we don't need to create the sqlite database - it's created when the schema is created + // let's only drop the .db file + + if ($dbPath = $connection->getParams()['path'] ?? null) { + (new Filesystem())->remove($dbPath); + } + continue; } if ($databasePlatform instanceof PostgreSQLPlatform) { // let's drop all connections to the database to be able to drop it - self::runCommand( - $application, - 'dbal:run-sql', - [ - '--connection' => $connectionName, - 'sql' => 'SELECT pid, pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = current_database() AND pid <> pg_backend_pid()', - ], - canFail: true, - ); + $sql = 'SELECT pid, pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = current_database() AND pid <> pg_backend_pid()'; + runCommand($application, "dbal:run-sql --connection={$connectionName} '{$sql}'", canFail: true); } - self::runCommand( - $application, - 'doctrine:database:drop', - ['--connection' => $connectionName, '--force' => true, '--if-exists' => true] - ); + runCommand($application, "doctrine:database:drop --connection={$connectionName} --force --if-exists"); - self::runCommand($application, 'doctrine:database:create', ['--connection' => $connectionName]); + runCommand($application, "doctrine:database:create --connection={$connectionName}"); } } } diff --git a/src/ORM/ResetDatabase/MigrateDatabaseResetter.php b/src/ORM/ResetDatabase/MigrateDatabaseResetter.php index fd99b57b..083ae01f 100644 --- a/src/ORM/ResetDatabase/MigrateDatabaseResetter.php +++ b/src/ORM/ResetDatabase/MigrateDatabaseResetter.php @@ -5,8 +5,12 @@ namespace Zenstruck\Foundry\ORM\ResetDatabase; use Doctrine\Bundle\DoctrineBundle\Registry; +use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Component\HttpKernel\KernelInterface; +use function Zenstruck\Foundry\application; +use function Zenstruck\Foundry\runCommand; + /** * @internal * @author Nicolas PHILIPPE @@ -26,30 +30,30 @@ public function __construct( parent::__construct($registry, $managers, $connections); } - final public function resetBeforeEachTest(KernelInterface $kernel): void + public function resetBeforeFirstTest(KernelInterface $kernel): void { $this->resetWithMigration($kernel); } - public function resetBeforeFirstTest(KernelInterface $kernel): void + final public function resetBeforeEachTest(KernelInterface $kernel): void { $this->resetWithMigration($kernel); } private function resetWithMigration(KernelInterface $kernel): void { - $application = self::application($kernel); + $application = application($kernel); $this->dropAndResetDatabase($application); if (!$this->configurations) { - self::runCommand($application, 'doctrine:migrations:migrate'); + runCommand($application, 'doctrine:migrations:migrate'); return; } foreach ($this->configurations as $configuration) { - self::runCommand($application, 'doctrine:migrations:migrate', ['--configuration' => $configuration]); + runCommand($application, "doctrine:migrations:migrate --configuration={$configuration}"); } } } diff --git a/src/ORM/ResetDatabase/SchemaDatabaseResetter.php b/src/ORM/ResetDatabase/SchemaDatabaseResetter.php index b21d54dc..b927576c 100644 --- a/src/ORM/ResetDatabase/SchemaDatabaseResetter.php +++ b/src/ORM/ResetDatabase/SchemaDatabaseResetter.php @@ -7,6 +7,9 @@ use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Component\HttpKernel\KernelInterface; +use function Zenstruck\Foundry\application; +use function Zenstruck\Foundry\runCommand; + /** * @internal * @author Nicolas PHILIPPE @@ -15,7 +18,7 @@ final class SchemaDatabaseResetter extends BaseOrmResetter implements OrmResette { final public function resetBeforeFirstTest(KernelInterface $kernel): void { - $application = self::application($kernel); + $application = application($kernel); $this->dropAndResetDatabase($application); $this->createSchema($application); @@ -23,7 +26,7 @@ final public function resetBeforeFirstTest(KernelInterface $kernel): void final public function resetBeforeEachTest(KernelInterface $kernel): void { - $application = self::application($kernel); + $application = application($kernel); $this->dropSchema($application); $this->createSchema($application); @@ -32,22 +35,14 @@ final public function resetBeforeEachTest(KernelInterface $kernel): void private function createSchema(Application $application): void { foreach ($this->managers as $manager) { - self::runCommand( - $application, - 'doctrine:schema:update', - ['--em' => $manager, '--force' => true] - ); + runCommand($application, "doctrine:schema:update --em={$manager} --force"); } } private function dropSchema(Application $application): void { foreach ($this->managers as $manager) { - self::runCommand( - $application, - 'doctrine:schema:drop', - ['--em' => $manager, '--force' => true, '--full-database' => true] - ); + runCommand($application, "doctrine:schema:drop --em={$manager} --force --full-database"); } } } diff --git a/src/Persistence/SymfonyCommandRunner.php b/src/Persistence/SymfonyCommandRunner.php deleted file mode 100644 index a83a26cb..00000000 --- a/src/Persistence/SymfonyCommandRunner.php +++ /dev/null @@ -1,40 +0,0 @@ - - */ -trait SymfonyCommandRunner -{ - /** - * @param array $parameters - */ - final protected static function runCommand(Application $application, string $command, array $parameters = [], bool $canFail = false): void - { - $exit = $application->run( - new ArrayInput(\array_merge(['command' => $command], $parameters + ['--no-interaction' => true])), - $output = new BufferedOutput() - ); - - if (0 !== $exit && !$canFail) { - throw new \RuntimeException(\sprintf('Error running "%s": %s', $command, $output->fetch())); - } - } - - final protected static function application(KernelInterface $kernel): Application - { - $application = new Application($kernel); - $application->setAutoExit(false); - - return $application; - } -} diff --git a/src/symfony_console.php b/src/symfony_console.php new file mode 100644 index 00000000..4ace905c --- /dev/null +++ b/src/symfony_console.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zenstruck\Foundry; + +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Component\Console\Input\StringInput; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * @internal + */ +function runCommand(Application $application, string $command, bool $canFail = false): void +{ + $exit = $application->run(new StringInput("$command --no-interaction"), $output = new BufferedOutput()); + + if (0 !== $exit && !$canFail) { + throw new \RuntimeException(\sprintf('Error running "%s": %s', $command, $output->fetch())); + } +} + +/** + * @internal + */ +function application(KernelInterface $kernel): Application +{ + $application = new Application($kernel); + $application->setAutoExit(false); + + return $application; +} diff --git a/tests/Fixture/CustomMigrations/Version20240611065130.php b/tests/Fixture/CustomMigrations/Version20240611065130.php deleted file mode 100644 index 9d51237f..00000000 --- a/tests/Fixture/CustomMigrations/Version20240611065130.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -// to "Migrations" directory on boot (cf. bootstrap.php) - -namespace Zenstruck\Foundry\Tests\Fixture\Migrations; - -use Doctrine\DBAL\Schema\Schema; -use Doctrine\Migrations\AbstractMigration; -use Zenstruck\Foundry\Tests\Fixture\EdgeCases\Migrate\ORM\EntityInAnotherSchema\Article; - -/** - * Create custom "cms" schema ({@see Article}) to ensure "migrate" mode is still working with multiple schemas. - * Note: the doctrine:migrations:diff command doesn't seem able to add this custom "CREATE SCHEMA" automatically. - * - * @see https://github.com/zenstruck/foundry/issues/618 - */ -final class Version20240611065130 extends AbstractMigration -{ - public function getDescription(): string - { - return 'Create custom "cms" schema.'; - } - - public function up(Schema $schema): void - { - $this->addSql('CREATE SCHEMA cms'); - } - - public function down(Schema $schema): void - { - $this->addSql('DROP SCHEMA cms'); - } -} diff --git a/tests/Fixture/EdgeCases/Migrate/ORM/EntityInAnotherSchema/Article.php b/tests/Fixture/MigrationTests/EntityInAnotherSchema/Article.php similarity index 67% rename from tests/Fixture/EdgeCases/Migrate/ORM/EntityInAnotherSchema/Article.php rename to tests/Fixture/MigrationTests/EntityInAnotherSchema/Article.php index fba361ef..f03800b8 100644 --- a/tests/Fixture/EdgeCases/Migrate/ORM/EntityInAnotherSchema/Article.php +++ b/tests/Fixture/MigrationTests/EntityInAnotherSchema/Article.php @@ -11,11 +11,17 @@ * file that was distributed with this source code. */ -namespace Zenstruck\Foundry\Tests\Fixture\EdgeCases\Migrate\ORM\EntityInAnotherSchema; +namespace Zenstruck\Foundry\Tests\Fixture\MigrationTests\EntityInAnotherSchema; use Doctrine\ORM\Mapping as ORM; use Zenstruck\Foundry\Tests\Fixture\Model\Base; +/** + * Create custom "cms" schema ({@see Article}) to ensure "migrate" mode is still working with multiple schemas. + * Note: this entity is added to mapping only for PostgreSQ, as it is the only supported DBMS which handles multiple schemas. + * + * @see https://github.com/zenstruck/foundry/issues/618 + */ #[ORM\Entity] #[ORM\Table(name: 'article', schema: 'cms')] class Article extends Base diff --git a/tests/Fixture/MigrationTests/TestMigrationKernel.php b/tests/Fixture/MigrationTests/TestMigrationKernel.php new file mode 100644 index 00000000..0546d7db --- /dev/null +++ b/tests/Fixture/MigrationTests/TestMigrationKernel.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zenstruck\Foundry\Tests\Fixture\MigrationTests; + +use DAMA\DoctrineTestBundle\DAMADoctrineTestBundle; +use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; +use Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle; +use Psr\Log\NullLogger; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Kernel; +use Zenstruck\Foundry\ORM\AbstractORMPersistenceStrategy; +use Zenstruck\Foundry\ORM\ResetDatabase\ResetDatabaseMode; +use Zenstruck\Foundry\Tests\Fixture\Stories\GlobalInvokableService; +use Zenstruck\Foundry\Tests\Fixture\Stories\GlobalStory; +use Zenstruck\Foundry\ZenstruckFoundryBundle; + +/** + * @author Nicolas PHILIPPE + */ +final class TestMigrationKernel extends Kernel +{ + use MicroKernelTrait; + + public function registerBundles(): iterable + { + yield new FrameworkBundle(); + yield new DoctrineBundle(); + yield new DoctrineMigrationsBundle(); + + yield new ZenstruckFoundryBundle(); + + if (\getenv('USE_DAMA_DOCTRINE_TEST_BUNDLE')) { + yield new DAMADoctrineTestBundle(); + } + } + + protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader): void + { + $c->loadFromExtension('framework', [ + 'http_method_override' => false, + 'secret' => 'S3CRET', + 'router' => ['utf8' => true], + 'test' => true, + ]); + + $c->loadFromExtension('zenstruck_foundry', [ + 'global_state' => [ + GlobalStory::class, + GlobalInvokableService::class, + ], + 'orm' => [ + 'reset' => [ + 'mode' => ResetDatabaseMode::MIGRATE, + 'migrations' => [ + 'configurations' => ($configFile = \getenv('WITH_MIGRATION_CONFIGURATION_FILE')) ? [$configFile] : [], + ], + ], + ], + ]); + + if (!\getenv('WITH_MIGRATION_CONFIGURATION_FILE')) { + $c->loadFromExtension('doctrine_migrations', include __DIR__.'/configs/migration-configuration.php'); + } + + $c->loadFromExtension('doctrine', [ + 'dbal' => ['url' => '%env(resolve:DATABASE_URL)%', 'use_savepoints' => true], + 'orm' => [ + 'auto_generate_proxy_classes' => true, + 'auto_mapping' => true, + 'mappings' => [ + 'Entity' => [ + 'is_bundle' => false, + 'type' => 'attribute', + 'dir' => '%kernel.project_dir%/tests/Fixture/Entity', + 'prefix' => 'Zenstruck\Foundry\Tests\Fixture\Entity', + 'alias' => 'Entity', + ], + 'Model' => [ + 'is_bundle' => false, + 'type' => 'attribute', + 'dir' => '%kernel.project_dir%/tests/Fixture/Model', + 'prefix' => 'Zenstruck\Foundry\Tests\Fixture\Model', + 'alias' => 'Model', + ], + ], + 'controller_resolver' => ['auto_mapping' => true], + ], + ]); + + // doctrine only handles different schema with postgres + if (str_starts_with(\getenv('DATABASE_URL') ?: '', 'postgresql')) { + $c->loadFromExtension('doctrine', [ + 'orm' => [ + 'mappings' => [ + 'Migrate' => [ + 'is_bundle' => false, + 'type' => 'attribute', + 'dir' => '%kernel.project_dir%/tests/Fixture/MigrationTests/EntityInAnotherSchema', + 'prefix' => 'Zenstruck\Foundry\Tests\Fixture\MigrationTests\EntityInAnotherSchema', + 'alias' => 'Migrate', + ], + ], + ], + ]); + } + + $c->register('logger', NullLogger::class); + $c->register(GlobalInvokableService::class); + } +} diff --git a/tests/Fixture/MigrationTests/configs/migration-configuration-transactional.php b/tests/Fixture/MigrationTests/configs/migration-configuration-transactional.php new file mode 100644 index 00000000..f404363d --- /dev/null +++ b/tests/Fixture/MigrationTests/configs/migration-configuration-transactional.php @@ -0,0 +1,8 @@ + [ + 'Zenstruck\\Foundry\\Tests\\Fixture\\MigrationTests\\Migrations' => dirname(__DIR__).'/Migrations', + ], + 'transactional' => true, +]; diff --git a/tests/Fixture/MigrationTests/configs/migration-configuration.php b/tests/Fixture/MigrationTests/configs/migration-configuration.php new file mode 100644 index 00000000..96533496 --- /dev/null +++ b/tests/Fixture/MigrationTests/configs/migration-configuration.php @@ -0,0 +1,8 @@ + [ + 'Zenstruck\\Foundry\\Tests\\Fixture\\MigrationTests\\Migrations' => dirname(__DIR__).'/Migrations', + ], + 'transactional' => false, +]; diff --git a/tests/Fixture/TestKernel.php b/tests/Fixture/TestKernel.php index 1f22c4dc..db6ba80d 100644 --- a/tests/Fixture/TestKernel.php +++ b/tests/Fixture/TestKernel.php @@ -13,7 +13,6 @@ use DAMA\DoctrineTestBundle\DAMADoctrineTestBundle; use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; -use Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle; use Doctrine\Bundle\MongoDBBundle\DoctrineMongoDBBundle; use Psr\Log\NullLogger; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; @@ -23,7 +22,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; -use Zenstruck\Foundry\ORM\AbstractORMPersistenceStrategy; use Zenstruck\Foundry\ORM\ResetDatabase\ResetDatabaseMode; use Zenstruck\Foundry\Tests\Fixture\Factories\ArrayFactory; use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory; @@ -45,7 +43,6 @@ public function registerBundles(): iterable if (\getenv('DATABASE_URL')) { yield new DoctrineBundle(); - yield new DoctrineMigrationsBundle(); } if (\getenv('MONGO_URL')) { @@ -75,7 +72,7 @@ protected function configureContainer(ContainerBuilder $c, LoaderInterface $load ], 'orm' => [ 'reset' => [ - 'mode' => \getenv('DATABASE_RESET_MODE') ?: ResetDatabaseMode::SCHEMA, + 'mode' => ResetDatabaseMode::SCHEMA, ], ], ]); @@ -106,27 +103,22 @@ protected function configureContainer(ContainerBuilder $c, LoaderInterface $load ], ]); - if (ResetDatabaseMode::MIGRATE->value === \getenv('DATABASE_RESET_MODE')) { + // doctrine only handles different schema with postgres + if (str_starts_with(\getenv('DATABASE_URL'), 'postgresql')) { $c->loadFromExtension('doctrine', [ 'orm' => [ 'mappings' => [ 'Migrate' => [ 'is_bundle' => false, 'type' => 'attribute', - 'dir' => '%kernel.project_dir%/tests/Fixture/EdgeCases/Migrate/ORM', - 'prefix' => 'Zenstruck\Foundry\Tests\Fixture\EdgeCases\Migrate\ORM', + 'dir' => '%kernel.project_dir%/tests/Fixture/MigrationTests/EntityInAnotherSchema', + 'prefix' => 'Zenstruck\Foundry\Tests\Fixture\MigrationTests\EntityInAnotherSchema', 'alias' => 'Migrate', ], ], ], ]); } - - $c->loadFromExtension('doctrine_migrations', [ - 'migrations_paths' => [ - 'Zenstruck\\Foundry\\Tests\\Fixture\\Migrations' => '%kernel.project_dir%/tests/Fixture/Migrations', - ], - ]); } if (\getenv('MONGO_URL')) { diff --git a/tests/Integration/Migration/ResetDatabaseWithMigrationTest.php b/tests/Integration/Migration/ResetDatabaseWithMigrationTest.php new file mode 100644 index 00000000..738d1f35 --- /dev/null +++ b/tests/Integration/Migration/ResetDatabaseWithMigrationTest.php @@ -0,0 +1,96 @@ + + */ +final class ResetDatabaseWithMigrationTest extends KernelTestCase +{ + use Factories; + use ResetDatabase; + use RequiresORM; + + /** + * @test + */ + public function it_generates_valid_schema(): void + { + $application = new Application(self::bootKernel()); + $application->setAutoExit(false); + + $exit = $application->run( + new ArrayInput(['command' => 'doctrine:schema:validate', '-v' => true]), + $output = new BufferedOutput() + ); + + // The command actually fails, because of a bug in doctrine ORM 3! + // https://github.com/doctrine/migrations/issues/1406 + self::assertSame(2, $exit, \sprintf('Schema is not valid: %s', $commandOutput = $output->fetch())); + self::assertStringContainsString('1 schema diff(s) detected', $commandOutput); + self::assertStringContainsString('DROP TABLE doctrine_migration_versions', $commandOutput); + } + + /** + * @test + */ + public function it_can_store_object(): void + { + StandardContactFactory::assert()->count(0); + + StandardContactFactory::createOne(); + + StandardContactFactory::assert()->count(1); + } + + /** + * @test + * @depends it_can_store_object + */ + public function it_starts_from_fresh_db(): void + { + StandardContactFactory::assert()->count(0); + } + + /** + * @test + */ + public function global_objects_are_created(): void + { + repository(GlobalEntity::class)->assert()->count(2); + } + + /** + * @test + */ + public function can_create_object_in_another_schema(): void + { + if (!str_starts_with(\getenv('DATABASE_URL') ?: '', 'postgresql')) { + self::markTestSkipped('PostgreSQL needed.'); + } + + persist(Article::class, ['title' => 'Hello World!']); + repository(Article::class)->assert()->count(1); + } + + protected static function getKernelClass(): string + { + return TestMigrationKernel::class; + } +} diff --git a/tests/bootstrap-migrate.php b/tests/bootstrap-migrate.php new file mode 100644 index 00000000..b319d778 --- /dev/null +++ b/tests/bootstrap-migrate.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Component\Dotenv\Dotenv; +use Symfony\Component\ErrorHandler\ErrorHandler; +use Symfony\Component\Filesystem\Filesystem; +use Zenstruck\Foundry\Tests\Fixture\MigrationTests\TestMigrationKernel; + +use function Zenstruck\Foundry\application; +use function Zenstruck\Foundry\runCommand; + +require \dirname(__DIR__) . '/vendor/autoload.php'; + +$fs = new Filesystem(); + +$fs->remove(__DIR__.'/../var'); + +(new Dotenv())->usePutenv()->loadEnv(__DIR__ . '/../.env'); + +$fs->remove(__DIR__ . '/Fixture/MigrationTests/Migrations'); +$fs->mkdir(__DIR__ . '/Fixture/MigrationTests/Migrations'); + +$kernel = new TestMigrationKernel('test', true); +$kernel->boot(); + +$application = application($kernel); + +runCommand($application, 'doctrine:database:drop --if-exists --force', canFail: true); +runCommand($application, 'doctrine:database:create', canFail: true); + +$configuration = ''; +if (getenv('WITH_MIGRATION_CONFIGURATION_FILE')) { + $configuration = '--configuration '.getcwd().'/'.getenv('WITH_MIGRATION_CONFIGURATION_FILE'); +} +runCommand($application, "doctrine:migrations:diff {$configuration}"); +runCommand($application, 'doctrine:database:drop --force', canFail: true); + +$kernel->shutdown(); + +\set_exception_handler([new ErrorHandler(), 'handleException']); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 03193ea6..4d9bb4c6 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -9,15 +9,9 @@ * file that was distributed with this source code. */ -use Symfony\Bundle\FrameworkBundle\Console\Application; -use Symfony\Component\Console\Input\StringInput; -use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Dotenv\Dotenv; use Symfony\Component\ErrorHandler\ErrorHandler; use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\Finder\Finder; -use Zenstruck\Foundry\ORM\ResetDatabase\ResetDatabaseMode; -use Zenstruck\Foundry\Tests\Fixture\TestKernel; require \dirname(__DIR__).'/vendor/autoload.php'; @@ -27,29 +21,4 @@ (new Dotenv())->usePutenv()->loadEnv(__DIR__.'/../.env'); -if (\getenv('DATABASE_URL') && ResetDatabaseMode::MIGRATE->value === \getenv('DATABASE_RESET_MODE')) { - $fs->remove(__DIR__.'/Fixture/Migrations'); - $fs->mkdir(__DIR__.'/Fixture/Migrations'); - - $kernel = new TestKernel('test', true); - $kernel->boot(); - - $application = new Application($kernel); - $application->setAutoExit(false); - - $application->run(new StringInput('doctrine:database:drop --if-exists --force'), new NullOutput()); - $application->run(new StringInput('doctrine:database:create'), new NullOutput()); - $application->run(new StringInput('doctrine:migrations:diff'), new NullOutput()); - $application->run(new StringInput('doctrine:database:drop --force'), new NullOutput()); - - // restore custom migrations - // this must be after "doctrine:migrations:diff" otherwise - // Doctrine is not able to run its diff command - foreach ((new Finder())->files()->in(__DIR__.'/Fixture/CustomMigrations') as $customMigrationFile) { - $fs->copy($customMigrationFile->getRealPath(), __DIR__.'/Fixture/Migrations/'.$customMigrationFile->getFilename()); - } - - $kernel->shutdown(); -} - \set_exception_handler([new ErrorHandler(), 'handleException']);