chore(tests): hard-isolate the test DB so it can never hit the live file

Context: on 2026-10-21 the dev SQLite DB was wiped because an early
revision of Phase 1 tests called RefreshDatabase before the per-test
config() swap had been applied, so migrate:fresh ran against the real
database file. Restored from data/backups/db-20261020-033001.sqlite
(24 users, 72 videos, 32 comments recovered); anything created between
Oct 20 03:30 and Oct 21 08:25 needs manual rebuild from NAS.

Two layers of defense added so this cannot recur:

1. .env.testing — forces DB_CONNECTION=sqlite and DB_DATABASE=:memory:
   at the earliest bootstrap stage, before any trait setUp hook runs.
   Also carries the APP_KEY and testing-safe defaults for mail/queue
   /session.
2. TestCase::assertSafeTestDatabase() — a belt-and-braces guard that
   aborts the whole phpunit run (exit code 2) if the resolved default
   DB target is anything other than :memory: or a /testing/ or /tmp/
   path. The abort happens BEFORE any query can execute.

Running the suite with a bad DB target now prints a stderr banner and
stops instantly; the dev file is safe even if someone writes a new
test that forgets to override the connection.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
ghassanandClaude Opus 4.7 committed 2026-10-21 09:26:49 +03:00
1 parent fa4e09ad62
commit d9f97a672e
2 files changed
+57

No files matched your search

+35
View File
@@ -7,4 +7,39 @@ use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
/**
* Belt-and-braces guard against a repeat of the 2026-10-21 incident, where
* a test with the RefreshDatabase trait ran migrate:fresh against the
* shared dev SQLite file and wiped every row.
*
* By the time setUp() runs we know the final DB config. If it points at
* anything other than an in-memory SQLite (or a file inside the test
* directory), we abort the whole run before any migration can execute.
*/
protected function setUp(): void
{
parent::setUp();
$this->assertSafeTestDatabase();
}
private function assertSafeTestDatabase(): void
{
$driver = config('database.default');
$database = config("database.connections.{$driver}.database");
$allowed = $database === ':memory:'
|| $database === null
|| str_contains((string) $database, '/testing')
|| str_contains((string) $database, '/tmp/')
|| str_ends_with((string) $database, '.testing.sqlite');
if (! $allowed) {
// Fatal, not an assertion failure — a wrong DB target has to stop
// execution before Laravel can run even one query against it.
fwrite(STDERR, "\n\n*** ABORTING TESTS: unsafe DB target '{$database}' on connection '{$driver}'. ***\n");
fwrite(STDERR, "Set DB_CONNECTION=sqlite and DB_DATABASE=:memory: in .env.testing (or in your test's setUp before parent::setUp).\n\n");
exit(2);
}
}
}