diff --git a/src/pobsync_backend/retention.py b/src/pobsync_backend/retention.py index e986d8d..0d17a45 100644 --- a/src/pobsync_backend/retention.py +++ b/src/pobsync_backend/retention.py @@ -78,8 +78,11 @@ def run_sql_retention_apply( def _do_apply() -> dict[str, Any]: plan = run_sql_retention_plan(host=host, kind=kind, protect_bases=bool(protect_bases)) delete_list = plan.get("delete") or [] + incomplete_list = plan.get("incomplete") or [] if not isinstance(delete_list, list): raise ConfigError("Invalid retention plan output: delete is not a list") + if not isinstance(incomplete_list, list): + raise ConfigError("Invalid retention plan output: incomplete is not a list") if max_delete == 0 and len(delete_list) > 0: raise ConfigError("Deletion blocked by --max-delete=0") if len(delete_list) > max_delete: @@ -116,6 +119,8 @@ def run_sql_retention_apply( "protect_bases": bool(protect_bases), "max_delete": max_delete, "source": "sql", + "planned_delete_count": len(delete_list), + "incomplete_ignored_count": len(incomplete_list), "deleted": deleted, "actions": actions, } diff --git a/src/pobsync_backend/templates/pobsync_backend/run_detail.html b/src/pobsync_backend/templates/pobsync_backend/run_detail.html index da7ffff..6d82121 100644 --- a/src/pobsync_backend/templates/pobsync_backend/run_detail.html +++ b/src/pobsync_backend/templates/pobsync_backend/run_detail.html @@ -172,7 +172,20 @@
Status: {% if prune_result.ok %}ok{% else %}warning{% endif %}
{% if prune_result.source %}
Source: {{ prune_result.source }}
{% endif %} + {% if prune_result.kind %}
Kind: {{ prune_result.kind }}
{% endif %} + {% if prune_result.planned_delete_count is not None %}
Planned deletions: {{ prune_result.planned_delete_count }}
{% endif %} {% if prune_result.deleted %}
Deleted: {{ prune_result.deleted|length }}
{% endif %} + {% if prune_result.max_delete is not None %}
Max delete: {{ prune_result.max_delete }}
{% endif %} + {% if prune_result.protect_bases is not None %}
Protect bases: {{ prune_result.protect_bases|yesno:"yes,no" }}
{% endif %} + {% if prune_result.incomplete_ignored_count %}
Incomplete ignored: {{ prune_result.incomplete_ignored_count }}
{% endif %} + {% if prune_result.actions %} +
Actions:
+ + {% endif %} {% if prune_result.error %}
Error: {{ prune_result.error }}
{% endif %} {% if prune_result.type %}
Type: {{ prune_result.type }}
{% endif %}
diff --git a/src/pobsync_backend/tests/test_sql_retention.py b/src/pobsync_backend/tests/test_sql_retention.py index 33e1c86..bdb903f 100644 --- a/src/pobsync_backend/tests/test_sql_retention.py +++ b/src/pobsync_backend/tests/test_sql_retention.py @@ -88,6 +88,9 @@ class SqlRetentionTests(TestCase): self.assertTrue(SnapshotRecord.objects.filter(pk=new.pk).exists()) self.assertFalse(SnapshotRecord.objects.filter(pk=old.pk).exists()) self.assertEqual(result["deleted"], [{"dirname": old.dirname, "kind": "scheduled", "path": str(old_dir)}]) + self.assertEqual(result["planned_delete_count"], 1) + self.assertEqual(result["max_delete"], 1) + self.assertEqual(result["incomplete_ignored_count"], 0) def test_apply_deletes_snapshot_with_readonly_data_directory(self) -> None: with TemporaryDirectory() as tmp: diff --git a/src/pobsync_backend/tests/test_views.py b/src/pobsync_backend/tests/test_views.py index c6e9cc5..e5f80b4 100644 --- a/src/pobsync_backend/tests/test_views.py +++ b/src/pobsync_backend/tests/test_views.py @@ -1197,9 +1197,15 @@ class ViewTests(TestCase): "hint": "Check network connectivity.", }, "prune": { - "ok": False, - "type": "ConfigError", - "error": "Deletion blocked by --max-delete=0", + "ok": True, + "source": "sql", + "kind": "scheduled", + "planned_delete_count": 1, + "max_delete": 1, + "protect_bases": True, + "incomplete_ignored_count": 1, + "deleted": [{"dirname": "20260518-021500Z__OLD"}], + "actions": ["deleted scheduled 20260518-021500Z__OLD"], }, }, ) @@ -1211,7 +1217,11 @@ class ViewTests(TestCase): self.assertContains(response, "transport") self.assertContains(response, "Check network connectivity.") self.assertContains(response, "Retention") - self.assertContains(response, "Deletion blocked by --max-delete=0") + self.assertContains(response, "Planned deletions") + self.assertContains(response, "Max delete") + self.assertContains(response, "Protect bases") + self.assertContains(response, "Incomplete ignored") + self.assertContains(response, "deleted scheduled 20260518-021500Z__OLD") def test_run_detail_surfaces_host_retention_warnings(self) -> None: self.client.force_login(self.staff_user)