Repository navigation
Replies: 1 comment
|
This seems like a valuable safeguard, but I have one question about the choice of Is I can imagine CI pipelines or custom test environments where Would validating the target database (or requiring an explicit opt-in for destructive refreshes) be a more robust safeguard than relying solely on the environment name? |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Description
Laravel's
RefreshDatabasetrait (Illuminate\Foundation\Testing\RefreshDatabase) runsmigrate:freshagainst whichever database connection PHPUnit/Laravel is currently using.The documented safety model assumes:
phpunit.xmlsetsAPP_ENV=testing:memory:) is configured thereIf PHPUnit is invoked with
--no-configuration, or without loadingphpunit.xml, those overrides are skipped. Laravel bootstraps from.envinstead (for exampleAPP_ENV=local,DB_DATABASE=my_app).In that situation,
RefreshDatabasecan runmigrate:freshagainst the developer's real local database with no warning.This is a common footgun:
vendor/bin/phpunit tests/Feature/ExampleTest.php --no-configurationas a shortcut--no-configurationas a fallback when a test command fails for unrelated reasonsProduction is partially protected by
DB::prohibitDestructiveCommands()(Laravel 11+), but local development is not. The docs say tests should only run whenAPP_ENV=testing, yetRefreshDatabasedoes not enforce that contract at runtime.Steps to reproduce
RefreshDatabaseon a feature test.envwithAPP_ENV=localandDB_DATABASE=my_real_dev_dbphpunit.xmlwithAPP_ENV=testingandDB_DATABASE=my_app_test(the safe setup)migrate:freshruns againstmy_real_dev_dbfrom.envinstead ofmy_app_testExpected behavior
RefreshDatabase(or the test bootstrap) should refuse to run destructive migrations unless the application environment istesting, with a clear error message pointing tophpunit.xml/php artisan test.Example error message:
Optionally, also verify the active database name differs from a configured production/dev name, or matches an explicit test database from config.
Actual behavior
migrate:freshruns silently against the.envdatabase. Local data is destroyed.Proposed solution
In
RefreshDatabase::migrateDatabases()(orrefreshTestDatabase()), before calling$this->artisan('migrate:fresh', ...):Consider also shipping a similar check in the default application
tests/TestCase.phpskeleton.An escape hatch (for example
RefreshDatabase::$force = true) could cover exotic CI setups.Environment
Additional context
We mitigated this in our applications with
Tests\Support\TestingEnvironmentGuardcalled fromTestCase::setUp()andcreateApplication(). This should arguably be framework-default behavior so every Laravel project gets the guard without copying custom code.Related docs already imply this contract:
phpunit.xml.env.testing/APP_ENV=testing:memory:SQLiteThe missing piece is runtime enforcement inside
RefreshDatabase, mirroring the spirit ofDB::prohibitDestructiveCommands()for production CLI.Related (searched 2026-07-05 — no exact duplicate open)
RefreshDatabasewhen misconfigured. Only targeted production (isProduction()). Taylor closed: PHPUnit should not be installed in production. Does not cover local dev +APP_ENV=local+--no-configuration.DB_URLin.envbypassedphpunit.xml→ tests/RefreshDatabasehit staging DB. Fixed in skeleton via laravel/laravel#6761. Different vector from--no-configuration.phpunit.xmlmust overrideDB_CONNECTION..env.testingvsphpunit.xmlprecedence — closed; team will not change override order.migrate:fresh/RefreshDatabasewith multiple DB connections — tangential for multi-DB apps.This proposal fills a gap: enforce the documented
APP_ENV=testingcontract insideRefreshDatabaseitself, for local and staging, not only production CLI.All reactions