mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-02 00:06:23 +00:00
fix: tell the user when a queued message gives up after unconfirmed attempts
This commit is contained in:
@@ -154,6 +154,39 @@ describe('offline send queue', () => {
|
|||||||
expect(useOutboxStore.getState().sends).toEqual([]);
|
expect(useOutboxStore.getState().sends).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('says so when a message fails after too many unconfirmed attempts, as for a refused one', async () => {
|
||||||
|
useNetworkStore.setState({ online: true, connected: true });
|
||||||
|
runQueuedSend.mockRejectedValueOnce(new Error('Invalid envelope'));
|
||||||
|
useOutboxStore.getState().enqueueSend(payload('refused'));
|
||||||
|
await flushed();
|
||||||
|
await flushed();
|
||||||
|
expect(toast.error).toHaveBeenCalledTimes(1);
|
||||||
|
expect(toast.error.mock.calls[0][1]).toEqual({ message: 'refused: Invalid envelope' });
|
||||||
|
|
||||||
|
toast.error.mockClear();
|
||||||
|
const id = useOutboxStore.getState().enqueueSend(payload('unconfirmed'))!;
|
||||||
|
useOutboxStore.setState({ sends: useOutboxStore.getState().sends.map((s) => (s.id === id ? { ...s, attempts: 2 } : s)) });
|
||||||
|
runQueuedSend.mockRejectedValueOnce(named('SendUnconfirmedError', 'Send confirmation was not received'));
|
||||||
|
useOutboxStore.setState({ flushing: false });
|
||||||
|
await useOutboxStore.getState().flush();
|
||||||
|
await flushed();
|
||||||
|
expect(useOutboxStore.getState().sends.find((s) => s.id === id)).toMatchObject({ status: 'failed', attempts: 3 });
|
||||||
|
expect(toast.error).toHaveBeenCalledTimes(1);
|
||||||
|
expect(toast.error.mock.calls[0]).toEqual([
|
||||||
|
'A message in the Outbox could not be sent', { message: 'unconfirmed: Send confirmation was not received' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not report an unconfirmed attempt that will be tried again', async () => {
|
||||||
|
useNetworkStore.setState({ online: true, connected: true });
|
||||||
|
runQueuedSend.mockRejectedValueOnce(named('SendUnconfirmedError', 'Send confirmation was not received'));
|
||||||
|
useOutboxStore.getState().enqueueSend(payload());
|
||||||
|
await flushed();
|
||||||
|
await flushed();
|
||||||
|
expect(useOutboxStore.getState().sends[0]).toMatchObject({ status: 'queued', attempts: 1 });
|
||||||
|
expect(toast.error).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('stops after an expired session without failing the message', async () => {
|
it('stops after an expired session without failing the message', async () => {
|
||||||
useNetworkStore.setState({ online: true, connected: true });
|
useNetworkStore.setState({ online: true, connected: true });
|
||||||
runQueuedSend.mockRejectedValueOnce(named('AuthenticationError', 'Session expired'));
|
runQueuedSend.mockRejectedValueOnce(named('AuthenticationError', 'Session expired'));
|
||||||
|
|||||||
@@ -690,14 +690,17 @@ async function flushSends(accountId: string): Promise<'done' | 'transient'> {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
const kind = classifySendFailure(err, isTransientNetworkError);
|
const kind = classifySendFailure(err, isTransientNetworkError);
|
||||||
const message = err instanceof Error ? err.message : String(err);
|
const message = err instanceof Error ? err.message : String(err);
|
||||||
updateSend(accountId, afterFailure(started, kind, message));
|
const next = afterFailure(started, kind, message);
|
||||||
|
updateSend(accountId, next);
|
||||||
|
// Refused, or unconfirmed too often: it waits for the user now, and
|
||||||
|
// nothing retries it, so say so whatever the reason.
|
||||||
|
if (next.status === 'failed') void notifyQueuedSendFailed(started, message);
|
||||||
if (kind === 'auth') {
|
if (kind === 'auth') {
|
||||||
useOutboxStore.setState({ paused: true });
|
useOutboxStore.setState({ paused: true });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (kind === 'offline' || kind === 'unconfirmed') return 'transient';
|
if (kind === 'offline' || kind === 'unconfirmed') return 'transient';
|
||||||
// Refused: this one waits for the user; the others go on.
|
// Refused: the others go on.
|
||||||
void notifyQueuedSendFailed(started, message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 'done';
|
return 'done';
|
||||||
|
|||||||
Reference in New Issue
Block a user