diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index 51a707915..186f85170 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -150,9 +150,10 @@ class AppServiceProvider extends ServiceProvider
$db_type = config()->get('database.default');
if ($db_type == 'sqlite') {
- $db_file = database_path(env('DB_DATABASE', 'app.sqlite'));
+ $db_file = config()->get('database.connections.sqlite.database');
Log::debug('SQLite Database Path: ' . $db_file);
- if (! is_file($db_file)) {
+ // Do not create a file for the in-memory database identifier.
+ if ($db_file !== ':memory:' && ! is_file($db_file)) {
touch($db_file);
}
}
diff --git a/config/database.php b/config/database.php
index 028c6bc86..5a21e148a 100644
--- a/config/database.php
+++ b/config/database.php
@@ -7,7 +7,11 @@ return [
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
- 'database' => database_path(env('DB_DATABASE', 'app.sqlite')), // Make sure to use the correct path
+ // Use the correct path, but let the special in-memory identifier
+ // pass through untouched so tests can run against ':memory:'.
+ 'database' => env('DB_DATABASE', 'app.sqlite') === ':memory:'
+ ? ':memory:'
+ : database_path(env('DB_DATABASE', 'app.sqlite')),
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), // Enable foreign key constraints
],
diff --git a/phpunit.xml b/phpunit.xml
index 909feb8dc..348ed5931 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -12,8 +12,8 @@
-
-
+
+
diff --git a/tests/Feature/ItemExportTest.php b/tests/Feature/ItemExportTest.php
index 51b9d0c33..58c251793 100644
--- a/tests/Feature/ItemExportTest.php
+++ b/tests/Feature/ItemExportTest.php
@@ -5,6 +5,7 @@ namespace Tests\Feature;
use App\Item;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Date;
+use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class ItemExportTest extends TestCase
@@ -39,6 +40,18 @@ class ItemExportTest extends TestCase
public function test_exports_assigned_tag_titles_excluding_the_root_tag(): void
{
+ // Mirror the root/default dashboard row that production seeds (id 0),
+ // so the item_tag pivot's foreign key to items.id is satisfied on a
+ // fresh in-memory database.
+ DB::table('items')->insert([
+ 'id' => 0,
+ 'title' => 'app.dashboard',
+ 'url' => '',
+ 'type' => 1,
+ 'user_id' => 0,
+ 'pinned' => 0,
+ ]);
+
$item = Item::factory()
->create([
'title' => 'Tagged Item',
diff --git a/tests/TestCase.php b/tests/TestCase.php
index fe1ffc2ff..98e540cd8 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -6,5 +6,42 @@ use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
- //
+ protected function setUp(): void
+ {
+ parent::setUp();
+
+ $this->guardAgainstRealDatabase();
+ }
+
+ /**
+ * Refuse to run the test suite against anything other than an
+ * in-memory SQLite database.
+ *
+ * The suite uses RefreshDatabase (migrate:fresh), which would wipe
+ * whatever database it is pointed at. The only supported test
+ * configuration for this repo is sqlite + ':memory:'. Any other
+ * connection (a real sqlite file, mysql, pgsql, ...) is rejected so
+ * we can never destroy real data.
+ */
+ private function guardAgainstRealDatabase(): void
+ {
+ $default = config('database.default');
+ $driver = config("database.connections.{$default}.driver");
+ $database = config("database.connections.{$default}.database");
+
+ if ($driver === 'sqlite' && $database === ':memory:') {
+ return;
+ }
+
+ throw new \RuntimeException(sprintf(
+ 'Refusing to run tests: the default database connection (%s) is '
+ . 'driver "%s" pointing at "%s". Tests only run against an '
+ . 'in-memory SQLite database (driver "sqlite", database ":memory:") '
+ . 'to avoid wiping real data via RefreshDatabase. Check phpunit.xml '
+ . 'DB_CONNECTION/DB_DATABASE overrides.',
+ $default,
+ $driver,
+ is_scalar($database) ? (string) $database : gettype($database)
+ ));
+ }
}