(feature) add Django setup flow for initial pobsync configuration

Add staff-only UI routes for creating/editing the default GlobalConfig
and creating the first HostConfig from the dashboard.

Improve the empty dashboard state so a fresh database guides the user
towards the next useful setup action instead of only showing empty tables.

Cover the setup flow with view tests for empty state prompts, global
config creation, and host creation.
This commit is contained in:
2026-05-19 12:25:45 +02:00
parent 4dbde43465
commit 6bcc15c174
8 changed files with 290 additions and 8 deletions

View File

@@ -47,6 +47,102 @@ class ViewTests(TestCase):
self.assertContains(response, "20260519-021500Z__ABCDEFGH")
self.assertContains(response, "success")
def test_dashboard_prompts_for_global_config_when_database_is_empty(self) -> None:
self.client.force_login(self.staff_user)
response = self.client.get(reverse("dashboard"))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "No default global config exists yet.")
self.assertContains(response, reverse("edit_global_config"))
self.assertContains(response, "Create global config")
def test_dashboard_prompts_for_first_host_after_global_config_exists(self) -> None:
self.client.force_login(self.staff_user)
GlobalConfig.objects.create(name="default", backup_root="/opt/pobsync/backups")
response = self.client.get(reverse("dashboard"))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Global config is ready.")
self.assertContains(response, reverse("create_host_config"))
self.assertContains(response, "Add first host")
def test_global_config_form_creates_default_config(self) -> None:
self.client.force_login(self.staff_user)
response = self.client.post(
reverse("edit_global_config"),
{
"name": "default",
"backup_root": "/backups",
"pobsync_home": "/opt/pobsync",
"ssh_user": "backup",
"ssh_port": "2222",
"ssh_options": "StrictHostKeyChecking=no\nBatchMode=yes",
"rsync_binary": "rsync",
"rsync_args": "-a\n--numeric-ids",
"rsync_extra_args": "--delete",
"rsync_timeout_seconds": "60",
"rsync_bwlimit_kbps": "1000",
"default_source_root": "/srv",
"default_destination_subdir": "rootfs",
"excludes_default": "*.tmp\ncache/",
"retention_daily": "7",
"retention_weekly": "4",
"retention_monthly": "2",
"retention_yearly": "1",
},
follow=True,
)
self.assertRedirects(response, reverse("dashboard"))
self.assertContains(response, "Global config saved for default.")
config = GlobalConfig.objects.get(name="default")
self.assertEqual(config.backup_root, "/backups")
self.assertEqual(config.ssh_user, "backup")
self.assertEqual(config.ssh_port, 2222)
self.assertEqual(config.ssh_options, ["StrictHostKeyChecking=no", "BatchMode=yes"])
self.assertEqual(config.rsync_args, ["-a", "--numeric-ids"])
self.assertEqual(config.rsync_extra_args, ["--delete"])
self.assertEqual(config.excludes_default, ["*.tmp", "cache/"])
self.assertEqual(config.retention_daily, 7)
self.assertEqual(config.retention_yearly, 1)
def test_create_host_config_form_creates_host(self) -> None:
self.client.force_login(self.staff_user)
response = self.client.post(
reverse("create_host_config"),
{
"host": "web-01",
"address": "web-01.example.test",
"enabled": "on",
"ssh_user": "backup",
"ssh_port": "2222",
"source_root": "/srv",
"includes": "/srv/www\n/srv/db",
"excludes_add": "*.tmp",
"excludes_replace": "",
"rsync_extra_args": "--numeric-ids",
"retention_daily": "7",
"retention_weekly": "4",
"retention_monthly": "2",
"retention_yearly": "1",
},
follow=True,
)
self.assertRedirects(response, reverse("host_detail", args=["web-01"]))
self.assertContains(response, "Host config created for web-01.")
host = HostConfig.objects.get(host="web-01")
self.assertEqual(host.address, "web-01.example.test")
self.assertEqual(host.ssh_user, "backup")
self.assertEqual(host.includes, ["/srv/www", "/srv/db"])
self.assertEqual(host.excludes_add, ["*.tmp"])
self.assertEqual(host.rsync_extra_args, ["--numeric-ids"])
self.assertEqual(host.retention_weekly, 4)
def test_host_detail_renders_config_schedule_runs_and_snapshots(self) -> None:
self.client.force_login(self.staff_user)
host = HostConfig.objects.create(