Allow MySQL / Postgres tests to run in parallel

This commit is contained in:
Dan Harrin
2025-12-11 09:31:36 +00:00
parent f15e468656
commit a006bf2fcf
24 changed files with 94 additions and 114 deletions
+7 -2
View File
@@ -39,6 +39,7 @@ jobs:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: testing
POSTGRES_HOST_AUTH_METHOD: trust
ports:
- 5432:5432
options: --health-cmd=pg_isready --health-interval=10s --health-timeout=5s --health-retries=3
@@ -75,7 +76,11 @@ jobs:
echo "Waiting for Postgres ($i) ..." && sleep 2
done
echo "Postgres did not become ready in time" && exit 1
- name: Execute tests
run: ./vendor/bin/pest --configuration=phpunit.${{ matrix.db }}.xml
- name: Execute tests (parallel)
run: ./vendor/bin/pest --configuration=phpunit.${{ matrix.db }}.xml --parallel --exclude-group=commands
env:
DB_PASSWORD: password
- name: Execute command tests (serial)
run: ./vendor/bin/pest --configuration=phpunit.${{ matrix.db }}.xml --group=commands
env:
DB_PASSWORD: password
+8 -4
View File
@@ -83,10 +83,13 @@
],
"pint": "pint --config pint-strict-imports.json",
"rector": "rector",
"test:pest": "pest --parallel",
"test:sqlite": "pest --configuration=phpunit.sqlite.xml --parallel",
"test:mysql": "pest --configuration=phpunit.mysql.xml",
"test:pgsql": "pest --configuration=phpunit.pgsql.xml",
"test:pest": "pest --parallel --exclude-group=commands",
"test:sqlite": "pest --configuration=phpunit.sqlite.xml --parallel --exclude-group=commands",
"test:mysql": "pest --configuration=phpunit.mysql.xml --parallel --exclude-group=commands",
"test:pgsql": "pest --configuration=phpunit.pgsql.xml --parallel --exclude-group=commands",
"test:commands:sqlite": "pest --configuration=phpunit.sqlite.xml --group=commands",
"test:commands:mysql": "pest --configuration=phpunit.mysql.xml --group=commands",
"test:commands:pgsql": "pest --configuration=phpunit.pgsql.xml --group=commands",
"test:all-databases": [
"@test:sqlite",
"@test:mysql",
@@ -95,6 +98,7 @@
"test:phpstan": "phpstan analyse",
"test": [
"@test:sqlite",
"@test:commands:sqlite",
"@test:phpstan"
]
},
@@ -1,16 +1,10 @@
<?php
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
beforeEach(function (): void {
// No specific setup needed
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
uses(TestCase::class)->group('commands');
it('can generate a field class', function (): void {
$this->withoutMockingConsoleOutput();
@@ -1,16 +1,14 @@
<?php
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
uses(TestCase::class)->group('commands');
beforeEach(function (): void {
$this->withoutMockingConsoleOutput();
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
});
it('can generate a form schema class', function (): void {
$this->artisan('make:filament-form', [
@@ -1,16 +1,14 @@
<?php
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
uses(TestCase::class)->group('commands');
beforeEach(function (): void {
$this->withoutMockingConsoleOutput();
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
});
it('can generate a Livewire form component', function (): void {
$this->artisan('make:filament-livewire-form', [
@@ -1,16 +1,10 @@
<?php
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
beforeEach(function (): void {
// No specific setup needed
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
uses(TestCase::class)->group('commands');
it('can generate an entry class', function (): void {
$this->withoutMockingConsoleOutput();
@@ -2,11 +2,10 @@
use Filament\Support\Commands\FileGenerators\FileGenerationFlag;
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
uses(TestCase::class)->group('commands');
beforeEach(function (): void {
config()->set('filament.file_generation.flags', [
@@ -14,8 +13,7 @@ beforeEach(function (): void {
]);
$this->withoutMockingConsoleOutput();
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
});
it('can generate a cluster class', function (): void {
$this->artisan('make:filament-cluster', [
@@ -6,13 +6,12 @@ use Filament\Support\Commands\FileGenerators\FileGenerationFlag;
use Filament\Tests\TestCase;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Arr;
use Illuminate\Testing\PendingCommand;
use function PHPUnit\Framework\assertFileDoesNotExist;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
uses(TestCase::class)->group('commands');
beforeEach(function (): void {
config()->set('filament.file_generation.flags', [
@@ -24,8 +23,7 @@ beforeEach(function (): void {
]);
MakePageCommand::$shouldCheckModelsForSoftDeletes = false;
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
});
it('can generate a page class', function (): void {
$this->withoutMockingConsoleOutput();
@@ -4,11 +4,10 @@ use Filament\Commands\MakeRelationManagerCommand;
use Filament\Facades\Filament;
use Filament\Support\Commands\FileGenerators\FileGenerationFlag;
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
uses(TestCase::class)->group('commands');
beforeEach(function (): void {
config()->set('filament.file_generation.flags', [
@@ -52,8 +51,7 @@ beforeEach(function (): void {
];
MakeRelationManagerCommand::$shouldCheckModelsForSoftDeletes = false;
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
});
it('can generate a relation manager', function (): void {
$this->artisan('make:filament-relation-manager', [
@@ -3,11 +3,10 @@
use Filament\Commands\MakeResourceCommand;
use Filament\Support\Commands\FileGenerators\FileGenerationFlag;
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
uses(TestCase::class)->group('commands');
beforeEach(function (): void {
config()->set('filament.file_generation.flags', [
@@ -20,8 +19,7 @@ beforeEach(function (): void {
$this->withoutMockingConsoleOutput();
MakeResourceCommand::$shouldCheckModelsForSoftDeletes = false;
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
});
it('can generate a resource class', function (): void {
$this->artisan('make:filament-resource', [
@@ -10,12 +10,11 @@ use DateTimeImmutable;
use DateTimeInterface;
use Filament\Support\Commands\FileGenerators\FileGenerationFlag;
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use Spatie\LaravelSettings\Settings as BaseSettings;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
uses(TestCase::class)->group('commands');
beforeEach(function (): void {
config()->set('filament.file_generation.flags', [
@@ -24,8 +23,7 @@ beforeEach(function (): void {
]);
$this->withoutMockingConsoleOutput();
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
});
it('can generate a page class', function (): void {
$this->artisan('make:filament-settings-page', [
@@ -1,16 +1,14 @@
<?php
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
uses(TestCase::class)->group('commands');
beforeEach(function (): void {
$this->withoutMockingConsoleOutput();
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
});
it('can generate a cluster class', function (): void {
$this->artisan('make:filament-cluster', [
@@ -5,18 +5,16 @@ use Filament\Facades\Filament;
use Filament\Tests\TestCase;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Arr;
use Illuminate\Testing\PendingCommand;
use function PHPUnit\Framework\assertFileDoesNotExist;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
uses(TestCase::class)->group('commands');
beforeEach(function (): void {
MakePageCommand::$shouldCheckModelsForSoftDeletes = false;
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
});
it('can generate a page class', function (): void {
$this->withoutMockingConsoleOutput();
@@ -3,11 +3,10 @@
use Filament\Commands\MakeRelationManagerCommand;
use Filament\Facades\Filament;
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
uses(TestCase::class)->group('commands');
beforeEach(function (): void {
$this->withoutMockingConsoleOutput();
@@ -47,8 +46,7 @@ beforeEach(function (): void {
];
MakeRelationManagerCommand::$shouldCheckModelsForSoftDeletes = false;
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
});
it('can generate a relation manager', function (): void {
$this->artisan('make:filament-relation-manager', [
@@ -3,19 +3,17 @@
use Filament\Commands\MakeResourceCommand;
use Filament\Facades\Filament;
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileDoesNotExist;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
uses(TestCase::class)->group('commands');
beforeEach(function (): void {
$this->withoutMockingConsoleOutput();
MakeResourceCommand::$shouldCheckModelsForSoftDeletes = false;
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
});
it('can generate a resource class', function (): void {
$this->artisan('make:filament-resource', [
@@ -9,17 +9,15 @@ use DateTime;
use DateTimeImmutable;
use DateTimeInterface;
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use Spatie\LaravelSettings\Settings as BaseSettings;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
uses(TestCase::class)->group('commands');
beforeEach(function (): void {
$this->withoutMockingConsoleOutput();
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
});
it('can generate a page class', function (): void {
$this->artisan('make:filament-settings-page', [
@@ -1,16 +1,10 @@
<?php
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
beforeEach(function (): void {
// No specific setup needed
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
uses(TestCase::class)->group('commands');
it('can generate a component class', function (): void {
$this->withoutMockingConsoleOutput();
@@ -1,16 +1,14 @@
<?php
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
uses(TestCase::class)->group('commands');
beforeEach(function (): void {
$this->withoutMockingConsoleOutput();
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
});
it('can generate a Livewire schema component', function (): void {
$this->artisan('make:filament-livewire-schema', [
@@ -1,16 +1,10 @@
<?php
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
beforeEach(function (): void {
// No specific setup needed
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
uses(TestCase::class)->group('commands');
it('can generate a schema class', function (): void {
$this->withoutMockingConsoleOutput();
@@ -1,17 +1,11 @@
<?php
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileDoesNotExist;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
beforeEach(function (): void {
// No specific setup needed
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
uses(TestCase::class)->group('commands');
it('can generate a column class', function (): void {
$this->withoutMockingConsoleOutput();
@@ -1,16 +1,14 @@
<?php
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
uses(TestCase::class)->group('commands');
beforeEach(function (): void {
$this->withoutMockingConsoleOutput();
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
});
it('can generate a Livewire table component', function (): void {
$this->artisan('make:filament-livewire-table', [
@@ -1,16 +1,10 @@
<?php
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
beforeEach(function (): void {
// No specific setup needed
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
uses(TestCase::class)->group('commands');
it('can generate a table class', function (): void {
$this->withoutMockingConsoleOutput();
+44 -3
View File
@@ -37,6 +37,7 @@ use Livewire\LivewireServiceProvider;
use Orchestra\Testbench\Concerns\WithWorkbench;
use Orchestra\Testbench\TestCase as BaseTestCase;
use PDO;
use PDOException;
use RyanChandler\BladeCaptureDirective\BladeCaptureDirectiveServiceProvider;
abstract class TestCase extends BaseTestCase
@@ -91,6 +92,11 @@ abstract class TestCase extends BaseTestCase
__DIR__ . '/../resources/views',
]);
// Paratest sets TEST_TOKEN for each worker (0, 1, 2, etc.)
$testToken = env('TEST_TOKEN', '');
$dbSuffix = $testToken !== '' ? "_{$testToken}" : '';
$dbName = env('DB_DATABASE', 'testing') . $dbSuffix;
$app['config']->set('database.connections.sqlite', [
'driver' => 'sqlite',
'database' => env('DB_DATABASE', ':memory:'),
@@ -102,7 +108,7 @@ abstract class TestCase extends BaseTestCase
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('MYSQL_PORT', env('DB_PORT', '3306')),
'database' => env('DB_DATABASE', 'testing'),
'database' => $dbName,
'username' => env('MYSQL_USERNAME', env('DB_USERNAME', 'root')),
'password' => env('MYSQL_PASSWORD', env('DB_PASSWORD', '')),
'charset' => 'utf8mb4',
@@ -121,7 +127,7 @@ abstract class TestCase extends BaseTestCase
'driver' => 'pgsql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('PGSQL_PORT', env('DB_PORT', '5432')),
'database' => env('DB_DATABASE', 'testing'),
'database' => $dbName,
'username' => env('PGSQL_USERNAME', env('DB_USERNAME', 'postgres')),
'password' => env('PGSQL_PASSWORD', env('DB_PASSWORD', '')),
'charset' => 'utf8',
@@ -134,7 +140,42 @@ abstract class TestCase extends BaseTestCase
],
]);
$app['config']->set('database.default', env('DB_CONNECTION', 'testing'));
$connection = env('DB_CONNECTION', 'testing');
// Auto-create test databases for MySQL/PostgreSQL parallel workers
if ($dbSuffix !== '' && in_array($connection, ['mysql', 'pgsql'])) {
$this->ensureDatabaseExists($connection, $dbName, $app['config']->get("database.connections.{$connection}"));
}
$app['config']->set('database.default', $connection);
$app['config']->set('database.connections.testing', $app['config']->get('database.connections.sqlite'));
}
protected function ensureDatabaseExists(string $driver, string $dbName, array $config): void
{
try {
if ($driver === 'mysql') {
$pdo = new PDO(
"mysql:host={$config['host']};port={$config['port']}",
$config['username'],
$config['password'],
$config['options']
);
$pdo->exec("create database if not exists `{$dbName}` character set {$config['charset']} collate {$config['collation']}");
} elseif ($driver === 'pgsql') {
$pdo = new PDO(
"pgsql:host={$config['host']};port={$config['port']};dbname=postgres",
$config['username'],
$config['password'],
$config['options']
);
$result = $pdo->query("select 1 from pg_database where datname = '{$dbName}'");
if ($result->fetchColumn() === false) {
$pdo->exec("create database \"{$dbName}\"");
}
}
} catch (PDOException) {
// Database might already exist or connection failed - let it fail later with a clearer error
}
}
}
@@ -1,16 +1,10 @@
<?php
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use function PHPUnit\Framework\assertFileExists;
uses(TestCase::class);
beforeEach(function (): void {
// No specific setup needed
})
->skip((bool) Arr::get($_SERVER, 'PARATEST'), 'File generation tests cannot be run in parallel as they would share a filesystem and have the potential to conflict with each other.');
uses(TestCase::class)->group('commands');
it('can generate a custom widget class', function (): void {
$this->withoutMockingConsoleOutput();