Add CalDAV outbox recovery UI

This commit is contained in:
2026-08-02 15:57:01 +02:00
parent 4e05ab2c3e
commit 77a40ec2f9
14 changed files with 737 additions and 14 deletions
+61
View File
@@ -18,6 +18,7 @@ from govoplan_calendar.backend.caldav import (
from govoplan_calendar.backend.db.models import CalendarOutboxOperation
from govoplan_calendar.backend.outbox import (
SqlCalendarOutboxProvider,
calendar_outbox_operation_action_states,
claim_next_calendar_outbox_operation,
discard_calendar_outbox_operation,
dispatch_calendar_outbox,
@@ -508,6 +509,66 @@ class CalendarOutboxTests(unittest.TestCase):
operation_id=first.id,
)
def test_recovery_action_state_is_batched_and_respects_source_state(self) -> None:
event = self.create_local_event(summary="First")
self.session.commit()
operation = self.session.query(CalendarOutboxOperation).one()
state = calendar_outbox_operation_action_states(
self.session,
[operation],
)[operation.id]
self.assertFalse(state["retry"]["allowed"])
self.assertTrue(state["reconcile"]["allowed"])
self.assertTrue(state["discard"]["allowed"])
operation.status = "conflict"
self.source.sync_enabled = False
self.session.flush()
disabled = calendar_outbox_operation_action_states(
self.session,
[operation],
)[operation.id]
self.assertFalse(disabled["retry"]["allowed"])
self.assertIn("disabled", str(disabled["retry"]["reason"]))
self.assertFalse(disabled["reconcile"]["allowed"])
self.assertTrue(disabled["discard"]["allowed"])
self.source.sync_enabled = True
update_event(
self.session,
tenant_id="tenant-1",
user_id=None,
event_id=event.id,
payload=CalendarEventUpdateRequest(summary="Second"),
)
self.session.flush()
stale = calendar_outbox_operation_action_states(
self.session,
[operation],
)[operation.id]
self.assertFalse(any(item["allowed"] for item in stale.values()))
self.assertIn("newer desired state", str(stale["discard"]["reason"]))
def test_manual_retry_cannot_be_repeated_while_pending(self) -> None:
self.create_local_event()
self.session.commit()
operation = self.session.query(CalendarOutboxOperation).one()
operation.status = "dead"
self.session.flush()
retry_calendar_outbox_operation(
self.session,
tenant_id="tenant-1",
operation_id=operation.id,
)
with self.assertRaisesRegex(ValueError, "pending"):
retry_calendar_outbox_operation(
self.session,
tenant_id="tenant-1",
operation_id=operation.id,
)
def test_discard_latest_cancels_attempted_predecessor_chain(self) -> None:
event = self.create_local_event(summary="First")
self.session.commit()