diff --git a/app/Livewire/Project/Database/BackupEdit.php b/app/Livewire/Project/Database/BackupEdit.php index fc7be5295..1dfea9322 100644 --- a/app/Livewire/Project/Database/BackupEdit.php +++ b/app/Livewire/Project/Database/BackupEdit.php @@ -9,6 +9,7 @@ use Exception; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Auth; use Livewire\Attributes\Locked; use Livewire\Attributes\Validate; use Livewire\Component; @@ -84,17 +85,49 @@ class BackupEdit extends Component #[Validate(['required', 'int', 'min:60', 'max:36000'])] public int|string $timeout = 3600; + public function getListeners(): array + { + // Keep "Backup Now" in sync when the database starts/stops without a full page refresh. + $listeners = ['databaseUpdated' => 'refreshStatus']; + + $user = Auth::user(); + if (! $user) { + return $listeners; + } + + $listeners["echo-private:user.{$user->id},DatabaseStatusChanged"] = 'refreshStatus'; + + $team = $user->currentTeam(); + if ($team) { + $listeners["echo-private:team.{$team->id},ServiceChecked"] = 'refreshStatus'; + } + + return $listeners; + } + public function mount() { try { $this->authorize('view', $this->backup->database); $this->parameters = get_route_parameters(); $this->syncData(); + $this->refreshStatus(); } catch (Exception $e) { return handleError($e, $this); } } + public function refreshStatus(): void + { + $database = $this->backup->database; + if (! $database) { + return; + } + + $database->refresh(); + $this->status = $database->status; + } + public function syncData(bool $toModel = false) { if ($toModel) { diff --git a/tests/Feature/BackupEditValidationTest.php b/tests/Feature/BackupEditValidationTest.php index 1e4582124..152bbc6da 100644 --- a/tests/Feature/BackupEditValidationTest.php +++ b/tests/Feature/BackupEditValidationTest.php @@ -430,3 +430,59 @@ function createS3StorageForBackupEditValidationTest(Team|int $team, string $name expect($backup->save_s3)->toBeFalsy(); expect($backup->s3_storage_id)->toBe($secondS3->id); }); + +it('subscribes to database status broadcasts so Backup Now can refresh without a full page reload', function () { + $component = app(BackupEdit::class); + $method = new ReflectionMethod($component, 'getListeners'); + $method->setAccessible(true); + $listeners = (array) $method->invoke($component); + + expect($listeners) + ->toHaveKey("echo-private:user.{$this->user->id},DatabaseStatusChanged") + ->toHaveKey("echo-private:team.{$this->team->id},ServiceChecked") + ->toHaveKey('databaseUpdated'); +}); + +it('shows Backup Now after refresh when the database becomes running', function () { + $backup = createBackupForEditValidationTest($this->team, [ + 'enabled' => true, + ]); + $database = $backup->database; + $database->update(['status' => 'exited:unhealthy']); + + $component = Livewire::test(BackupEdit::class, [ + 'backup' => $backup->fresh(), + 'availableS3Storages' => $this->team->s3s, + 'status' => 'exited:unhealthy', + ]) + ->assertDontSee('Backup Now') + ->assertSet('status', 'exited:unhealthy'); + + $database->update(['status' => 'running:healthy']); + + $component->call('refreshStatus') + ->assertSet('status', 'running:healthy') + ->assertSee('Backup Now'); +}); + +it('hides Backup Now after refresh when the database stops', function () { + $backup = createBackupForEditValidationTest($this->team, [ + 'enabled' => true, + ]); + $database = $backup->database; + $database->update(['status' => 'running:healthy']); + + $component = Livewire::test(BackupEdit::class, [ + 'backup' => $backup->fresh(), + 'availableS3Storages' => $this->team->s3s, + 'status' => 'running:healthy', + ]) + ->assertSee('Backup Now') + ->assertSet('status', 'running:healthy'); + + $database->update(['status' => 'exited:unhealthy']); + + $component->call('refreshStatus') + ->assertSet('status', 'exited:unhealthy') + ->assertDontSee('Backup Now'); +});