(feature) Add live refresh for run detail status

Add a server-rendered run detail partial and a small vanilla JavaScript
refresh hook so active backup runs update status, controls, timing, and
rsync log output without a full page reload.

Document the Django-template-first refresh pattern for future control
panel work.

Refs #36
This commit is contained in:
2026-05-21 15:10:37 +02:00
parent 1d6c21764b
commit 3cac7b61ac
7 changed files with 249 additions and 152 deletions

View File

@@ -1706,6 +1706,46 @@ class ViewTests(TestCase):
self.assertContains(response, reverse("cancel_run", args=[run.id]))
self.assertContains(response, 'class="danger"', html=False)
def test_run_detail_enables_live_refresh_for_active_run(self) -> None:
self.client.force_login(self.staff_user)
host = HostConfig.objects.create(host="web-01", address="web-01.example.test")
run = BackupRun.objects.create(host=host, status=BackupRun.Status.RUNNING)
response = self.client.get(reverse("run_detail", args=[run.id]))
self.assertEqual(response.status_code, 200)
self.assertContains(response, f'data-refresh-url="{reverse("run_detail_live", args=[run.id])}"', html=False)
self.assertContains(response, 'data-refresh-interval="5000"', html=False)
self.assertContains(response, 'data-refresh-active="true"', html=False)
def test_run_detail_live_returns_partial_for_active_run(self) -> None:
self.client.force_login(self.staff_user)
host = HostConfig.objects.create(host="web-01", address="web-01.example.test")
run = BackupRun.objects.create(
host=host,
status=BackupRun.Status.RUNNING,
result={"rsync": {"log_tail": ["sending incremental file list"]}},
)
response = self.client.get(reverse("run_detail_live", args=[run.id]))
self.assertEqual(response.status_code, 200)
self.assertEqual(response["X-Pobsync-Refresh-Active"], "true")
self.assertContains(response, "Run Control")
self.assertContains(response, "sending incremental file list")
self.assertNotContains(response, "<html", html=False)
def test_run_detail_live_stops_refresh_for_terminal_run(self) -> None:
self.client.force_login(self.staff_user)
host = HostConfig.objects.create(host="web-01", address="web-01.example.test")
run = BackupRun.objects.create(host=host, status=BackupRun.Status.SUCCESS)
response = self.client.get(reverse("run_detail_live", args=[run.id]))
self.assertEqual(response.status_code, 200)
self.assertEqual(response["X-Pobsync-Refresh-Active"], "false")
self.assertNotContains(response, "Run Control")
def test_run_detail_renders_worker_execution_metadata(self) -> None:
self.client.force_login(self.staff_user)
host = HostConfig.objects.create(host="web-01", address="web-01.example.test")