@remit/imap-worker 0.0.47 → 0.0.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -125,6 +125,27 @@ const event: AppendSentMessageEvent = {
|
|
|
125
125
|
const called = (method: string): Call[] =>
|
|
126
126
|
h.calls.filter((c) => c.method === method);
|
|
127
127
|
|
|
128
|
+
type BackendClient = Awaited<ReturnType<AppendSentMessageDeps["getClient"]>>;
|
|
129
|
+
|
|
130
|
+
const depsWithFailingDelete = (): AppendSentMessageDeps => {
|
|
131
|
+
const base = deps();
|
|
132
|
+
return {
|
|
133
|
+
...base,
|
|
134
|
+
getClient: async (): Promise<BackendClient> => {
|
|
135
|
+
const client = await base.getClient();
|
|
136
|
+
return {
|
|
137
|
+
...client,
|
|
138
|
+
outboxMessage: {
|
|
139
|
+
...client.outboxMessage,
|
|
140
|
+
delete: async (): Promise<void> => {
|
|
141
|
+
throw new Error("storage down");
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
|
|
128
149
|
describe("handleAppendSentMessage", () => {
|
|
129
150
|
beforeEach(() => {
|
|
130
151
|
h = fresh();
|
|
@@ -268,19 +289,8 @@ describe("handleAppendSentMessage", () => {
|
|
|
268
289
|
});
|
|
269
290
|
|
|
270
291
|
it("leaves the row alone when the APPEND landed but the delete did not", async () => {
|
|
271
|
-
const failingDeps = deps();
|
|
272
|
-
const client = await failingDeps.getClient();
|
|
273
|
-
(
|
|
274
|
-
client as unknown as { outboxMessage: { delete: () => Promise<void> } }
|
|
275
|
-
).outboxMessage.delete = async () => {
|
|
276
|
-
throw new Error("storage down");
|
|
277
|
-
};
|
|
278
|
-
|
|
279
292
|
await assert.rejects(
|
|
280
|
-
handleAppendSentMessage(event, noopLog,
|
|
281
|
-
...failingDeps,
|
|
282
|
-
getClient: async () => client,
|
|
283
|
-
} as AppendSentMessageDeps),
|
|
293
|
+
handleAppendSentMessage(event, noopLog, 1, depsWithFailingDelete()),
|
|
284
294
|
/storage down/,
|
|
285
295
|
);
|
|
286
296
|
|
|
@@ -288,4 +298,18 @@ describe("handleAppendSentMessage", () => {
|
|
|
288
298
|
// would say the opposite.
|
|
289
299
|
assert.equal(called("outboxMessage.update").length, 0);
|
|
290
300
|
});
|
|
301
|
+
|
|
302
|
+
it("stops redelivering a landed APPEND at the budget instead of filing another copy", async () => {
|
|
303
|
+
// A redelivery starts from the top and appends again, so retrying past the
|
|
304
|
+
// budget files one copy per attempt in the user's Sent folder (#830).
|
|
305
|
+
await handleAppendSentMessage(
|
|
306
|
+
event,
|
|
307
|
+
noopLog,
|
|
308
|
+
APPEND_SENT_MAX_ATTEMPTS,
|
|
309
|
+
depsWithFailingDelete(),
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
assert.equal(called("connection.append").length, 1);
|
|
313
|
+
assert.equal(called("outboxMessage.update").length, 0);
|
|
314
|
+
});
|
|
291
315
|
});
|
|
@@ -193,16 +193,27 @@ export const handleAppendSentMessage = async (
|
|
|
193
193
|
// attempt to pick up. At the budget the record would dead-letter, and a
|
|
194
194
|
// dead-lettered APPEND is exactly how a delivered message goes missing.
|
|
195
195
|
//
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
|
|
196
|
+
// The budget binds whether or not the APPEND landed. A redelivery
|
|
197
|
+
// starts from the top and appends again, so an unbudgeted retry files
|
|
198
|
+
// one copy per attempt in the user's Sent folder.
|
|
199
|
+
if (receiveCount < APPEND_SENT_MAX_ATTEMPTS) throw error;
|
|
199
200
|
return error;
|
|
200
201
|
},
|
|
201
202
|
);
|
|
202
203
|
|
|
203
|
-
// The APPEND landed: the copy is in Sent
|
|
204
|
-
//
|
|
205
|
-
|
|
204
|
+
// The APPEND landed: the copy is in Sent whatever failed after it, so there
|
|
205
|
+
// is nothing to settle as unfiled. A row that outlives its delete holds
|
|
206
|
+
// `sent`, which every view hides, until the migrator's boot-time stranded-row
|
|
207
|
+
// repair settles it — the next container start, not sooner (#824).
|
|
208
|
+
if (appended) {
|
|
209
|
+
if (failure) {
|
|
210
|
+
log.error(
|
|
211
|
+
{ accountId, outboxMessageId, reason: String(failure) },
|
|
212
|
+
"Sent message was filed but its outbox row survived its delete and stays hidden until the boot-time repair",
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
206
217
|
|
|
207
218
|
// A terminal auth failure returns here without throwing — withOAuthLifecycle
|
|
208
219
|
// flips the account to reauth_required and ACKs the record, which without
|