Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Illuminate\Database\Schema\Builder as SchemaBuilder;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Foundation\Testing\DatabaseTruncation;
use Illuminate\Foundation\Testing\RefreshDatabaseState;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Queue;
Expand Down Expand Up @@ -64,6 +65,11 @@ abstract class TestCase extends BaseTestCase
*/
private static array $workers = [];

/**
* @var array<string, array{database: string, prefix: string, tables: list<string>}>|null
*/
private static ?array $truncationSchema = null;

/**
* @var array<int, array{
* stdout: string,
Expand Down Expand Up @@ -193,8 +199,25 @@ protected function advanceWithWorkflowHeartbeats(V2WorkflowTask $task, CarbonInt
Carbon::setTestNow($target);
}

protected function beforeTruncatingDatabase(): void
{
// RefreshDatabase's migrated flag is shared with standalone Testbench
// classes whose migration paths can omit the framework tables. A flag
// from that application cannot establish this harness's schema.
if (self::$truncationSchema !== $this->truncationSchema()) {
RefreshDatabaseState::$migrated = false;
}

if (! RefreshDatabaseState::$migrated) {
// A fresh migration also invalidates Laravel's per-connection table
// cache; retaining it can miss new tables or query removed ones.
static::$allTables = [];
}
}

protected function setUpDatabaseTruncation(): void
{
self::$truncationSchema = $this->truncationSchema();
// Testbench's PendingCommand binds a mocked OutputStyle while it runs
// migrations. Remove that per-command binding so application commands
// invoked by a test can write to the output buffer supplied by Artisan.
Expand Down Expand Up @@ -426,6 +449,30 @@ protected static function workerDiagnostics(): array
return $diagnostics;
}

/**
* @return array<string, array{database: string, prefix: string, tables: list<string>}>
*/
private function truncationSchema(): array
{
$schema = [];
foreach ($this->connectionsToTruncate() as $name) {
$connection = $this->app->make('db')
->connection($name);
$tables = $connection->getSchemaBuilder()
->getTableListing();
sort($tables);
$schema[$connection->getName()] = [
'database' => $connection->getDatabaseName(),
'prefix' => $connection->getTablePrefix(),
'tables' => $tables,
];
}

ksort($schema);

return $schema;
}

private static function normalizeJsonObject(array $value): array
{
$normalized = array_map(
Expand Down
51 changes: 51 additions & 0 deletions tests/Unit/TestDatabaseSchemaBoundaryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace Tests\Unit;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use PHPUnit\Framework\Attributes\Depends;
use Tests\TestCase;

final class TestDatabaseSchemaBoundaryTest extends TestCase
{
public function testFrameworkAndWorkflowTablesPrimeTheTruncationCache(): void
{
foreach (['users', 'jobs', 'workflow_instances', 'workflow_runs'] as $table) {
self::assertTrue(Schema::hasTable($table), "Expected [{$table}] to be migrated.");
}
}

#[Depends('testFrameworkAndWorkflowTablesPrimeTheTruncationCache')]
public function testAnotherMigrationScopeCanLeaveOnlyWorkflowTables(): void
{
// A standalone RefreshDatabase application can recreate the physical
// database with package migrations but without framework migrations.
Schema::drop('users');
Schema::drop('jobs');
DB::table('workflow_instances')->insert([
'id' => 'schema-boundary-probe',
'workflow_class' => self::class,
'workflow_type' => 'test.schema-boundary',
'run_count' => 0,
'created_at' => now(),
'updated_at' => now(),
]);

self::assertFalse(Schema::hasTable('users'));
self::assertFalse(Schema::hasTable('jobs'));
self::assertSame(1, DB::table('workflow_instances')->count());
}

#[Depends('testAnotherMigrationScopeCanLeaveOnlyWorkflowTables')]
public function testNextTestRestoresTheWholeSchemaAndClearsCommittedRows(): void
{
foreach (['users', 'jobs', 'workflow_instances', 'workflow_runs'] as $table) {
self::assertTrue(Schema::hasTable($table), "Expected [{$table}] to be restored.");
self::assertSame(0, DB::table($table)->count(), "Expected [{$table}] to be empty.");
}
self::assertGreaterThan(0, DB::table('migrations')->count());
}
}
Loading