review fixes

This commit is contained in:
Sylvain Zimmer
2026-07-15 11:10:48 +02:00
parent 8919d474e4
commit f82614bde5
2 changed files with 24 additions and 7 deletions
@@ -343,7 +343,7 @@ class TestImportUpdate:
def test_patch_continuous_with_pause_persists_mode(self, api_client, mailbox, user):
"""{mode: continuous, is_active: false} means "arm as a poller but
start it paused": the mode must be persisted (not silently dropped)
and no run dispatched, so a later is_active=true can resume it."""
and no run dispatched."""
channel = self._imap_import(mailbox, user)
with patch("core.api.viewsets.imports.run_import_task.delay") as mock_delay:
response = api_client.patch(
@@ -355,7 +355,16 @@ class TestImportUpdate:
assert response.data["mode"] == enums.ImportMode.CONTINUOUS.value
assert response.data["is_active"] is False
mock_delay.assert_not_called()
# And the paused poller can now actually be resumed.
def test_patch_resumes_paused_continuous_import(self, api_client, mailbox, user):
"""A poller armed but paused via {mode: continuous, is_active: false} can
later be resumed by is_active=true, which dispatches exactly one run."""
channel = self._imap_import(mailbox, user)
api_client.patch(
self._url(mailbox, channel),
{"mode": "continuous", "is_active": False},
format="json",
)
with patch("core.api.viewsets.imports.run_import_task.delay") as mock_delay:
response = api_client.patch(
self._url(mailbox, channel), {"is_active": True}, format="json"
@@ -124,7 +124,7 @@ const ImportIndicator = () => {
// importer modal's invalidations wake this query up on a new run.
refetchInterval: (query) => {
const rows = (query.state.data?.data as ImportRun[] | undefined) ?? [];
return rows.some((r) => !isTerminal(r.status)) ? 60000 : false;
return rows.some((r) => r.is_active && !isTerminal(r.status)) ? 60000 : false;
},
},
// Background status poll: let foreground requests win the wire.
@@ -134,21 +134,29 @@ const ImportIndicator = () => {
const activeRuns = useMemo(
() =>
((data?.data as ImportRun[] | undefined) ?? []).filter(
(r) => !isTerminal(r.status),
(r) => r.is_active && !isTerminal(r.status),
),
[data],
);
if (!selectedMailbox || activeRuns.length === 0) return null;
// Average across the runs that already know their total; indeterminate until
// at least one does. Capped below 100 — the button disappears on completion.
// Weighted average across the runs that already know their total, so a small
// run can't dominate the badge; indeterminate until at least one knows its
// total. Capped below 100 — the button disappears on completion.
const withTotal = activeRuns.filter((r) => (r.total_messages ?? 0) > 0);
const totalMessages = withTotal.reduce(
(sum, r) => sum + (r.total_messages ?? 0),
0,
);
const progress = withTotal.length
? Math.min(
99,
Math.round(
withTotal.reduce((sum, r) => sum + (r.progress ?? 0), 0) / withTotal.length,
withTotal.reduce(
(sum, r) => sum + (r.progress ?? 0) * (r.total_messages ?? 0),
0,
) / totalMessages,
),
)
: null;