@remit/imap-worker 0.0.90 → 0.0.91
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/dist/account-check.d.ts +27 -0
- package/dist/account-check.js +70 -0
- package/dist/body-sync-gate.d.ts +12 -0
- package/dist/body-sync-gate.js +53 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +193 -0
- package/dist/config-env.d.ts +5 -0
- package/dist/config-env.js +37 -0
- package/dist/connection-pool.d.ts +22 -0
- package/dist/connection-pool.js +97 -0
- package/dist/connection-scope.d.ts +8 -0
- package/dist/connection-scope.js +10 -0
- package/dist/context.d.ts +46 -0
- package/dist/context.js +15 -0
- package/dist/e2e-processor-shim.d.ts +2 -0
- package/dist/e2e-processor-shim.js +181 -0
- package/dist/emit.d.ts +15 -0
- package/dist/emit.js +76 -0
- package/dist/events.d.ts +203 -0
- package/dist/events.js +1 -0
- package/dist/failure-kind.d.ts +10 -0
- package/{src/failure-kind.ts → dist/failure-kind.js} +6 -6
- package/dist/handlers/append-sent-message.d.ts +3 -0
- package/dist/handlers/append-sent-message.js +152 -0
- package/dist/handlers/delete-account-objects.d.ts +3 -0
- package/dist/handlers/delete-account-objects.js +35 -0
- package/dist/handlers/empty-trash.d.ts +14 -0
- package/dist/handlers/empty-trash.js +217 -0
- package/dist/handlers/flag-push.d.ts +38 -0
- package/dist/handlers/flag-push.js +195 -0
- package/dist/handlers/imported-folder.d.ts +4 -0
- package/dist/handlers/imported-folder.js +23 -0
- package/dist/handlers/mailbox-management.d.ts +6 -0
- package/dist/handlers/mailbox-management.js +294 -0
- package/dist/handlers/mailbox-sync-order.d.ts +21 -0
- package/dist/handlers/mailbox-sync-order.js +50 -0
- package/dist/handlers/message-copy.d.ts +40 -0
- package/dist/handlers/message-copy.js +371 -0
- package/dist/handlers/message-delete-terminal.d.ts +60 -0
- package/dist/handlers/message-delete-terminal.js +77 -0
- package/dist/handlers/message-delete.d.ts +57 -0
- package/dist/handlers/message-delete.js +520 -0
- package/{src/handlers/message-move-terminal.ts → dist/handlers/message-move-terminal.d.ts} +18 -105
- package/dist/handlers/message-move-terminal.js +94 -0
- package/dist/handlers/message-move.d.ts +146 -0
- package/dist/handlers/message-move.js +490 -0
- package/dist/handlers/placement-move-push.d.ts +62 -0
- package/dist/handlers/placement-move-push.js +307 -0
- package/dist/handlers/probe-message-id.d.ts +2 -0
- package/dist/handlers/probe-message-id.js +6 -0
- package/dist/handlers/sync-mailboxes.d.ts +45 -0
- package/dist/handlers/sync-mailboxes.js +233 -0
- package/dist/handlers/sync-message-body.d.ts +23 -0
- package/dist/handlers/sync-message-body.js +182 -0
- package/dist/handlers/sync-messages.d.ts +29 -0
- package/dist/handlers/sync-messages.js +285 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +67 -0
- package/dist/lambda-context.d.ts +8 -0
- package/dist/lambda-context.js +33 -0
- package/dist/mailbox-row.d.ts +9 -0
- package/dist/mailbox-row.js +6 -0
- package/dist/poller.d.ts +1 -0
- package/dist/poller.js +51 -0
- package/dist/process.d.ts +6 -0
- package/dist/process.js +3 -0
- package/dist/processor.d.ts +3 -0
- package/dist/processor.js +44 -0
- package/dist/queues.d.ts +10 -0
- package/dist/queues.js +1 -0
- package/dist/s3-account-objects.d.ts +3 -0
- package/dist/s3-account-objects.js +26 -0
- package/dist/scheduler/attachment-sweep.d.ts +11 -0
- package/dist/scheduler/attachment-sweep.js +23 -0
- package/dist/scheduler/calendar-subscriptions.d.ts +26 -0
- package/dist/scheduler/calendar-subscriptions.js +35 -0
- package/dist/scheduler/config.d.ts +27 -0
- package/{src/scheduler/config.ts → dist/scheduler/config.js} +7 -26
- package/{src/scheduler/decide-due.ts → dist/scheduler/decide-due.d.ts} +3 -12
- package/dist/scheduler/decide-due.js +17 -0
- package/dist/scheduler/handler.d.ts +21 -0
- package/{src/scheduler/handler.ts → dist/scheduler/handler.js} +25 -42
- package/{src/scheduler/loop.ts → dist/scheduler/loop.d.ts} +7 -23
- package/dist/scheduler/loop.js +8 -0
- package/dist/scheduler/run-tick.d.ts +62 -0
- package/dist/scheduler/run-tick.js +164 -0
- package/dist/scheduler/runner.d.ts +2 -0
- package/{src/scheduler/runner.ts → dist/scheduler/runner.js} +57 -78
- package/dist/scheduler/stranded-placement-redrive.d.ts +16 -0
- package/dist/scheduler/stranded-placement-redrive.js +172 -0
- package/dist/sqs-queues.d.ts +4 -0
- package/dist/sqs-queues.js +22 -0
- package/dist/with-oauth-lifecycle-deps.d.ts +5 -0
- package/dist/with-oauth-lifecycle-deps.js +17 -0
- package/dist/with-oauth-lifecycle.d.ts +56 -0
- package/dist/with-oauth-lifecycle.js +92 -0
- package/{src/worker-version.ts → dist/worker-version.d.ts} +1 -2
- package/dist/worker-version.js +12 -0
- package/package.json +75 -69
- package/src/account-check.test.ts +0 -149
- package/src/account-check.ts +0 -101
- package/src/body-sync-gate.test.ts +0 -185
- package/src/body-sync-gate.ts +0 -86
- package/src/cli.ts +0 -218
- package/src/connection-scope.test.ts +0 -266
- package/src/connection-scope.ts +0 -314
- package/src/credential-less-sync-fence.test.ts +0 -152
- package/src/e2e-processor-shim.ts +0 -232
- package/src/emit.test.ts +0 -145
- package/src/emit.ts +0 -115
- package/src/events.ts +0 -252
- package/src/failure-kind.test.ts +0 -42
- package/src/handlers/append-sent-message.sqlite.test.ts +0 -176
- package/src/handlers/append-sent-message.ts +0 -264
- package/src/handlers/delete-account-objects.test.ts +0 -126
- package/src/handlers/delete-account-objects.ts +0 -96
- package/src/handlers/empty-trash.sqlite.test.ts +0 -209
- package/src/handlers/empty-trash.ts +0 -352
- package/src/handlers/flag-push.ts +0 -285
- package/src/handlers/imported-folder.test.ts +0 -63
- package/src/handlers/imported-folder.ts +0 -38
- package/src/handlers/mailbox-management.sqlite.test.ts +0 -265
- package/src/handlers/mailbox-management.ts +0 -486
- package/src/handlers/mailbox-sync-order.test.ts +0 -93
- package/src/handlers/mailbox-sync-order.ts +0 -65
- package/src/handlers/message-copy.sqlite.test.ts +0 -298
- package/src/handlers/message-copy.ts +0 -569
- package/src/handlers/message-delete-terminal.ts +0 -147
- package/src/handlers/message-delete.sqlite.test.ts +0 -291
- package/src/handlers/message-delete.ts +0 -888
- package/src/handlers/message-move.sqlite.test.ts +0 -271
- package/src/handlers/message-move.test.ts +0 -216
- package/src/handlers/message-move.ts +0 -755
- package/src/handlers/placement-move-push.test.ts +0 -137
- package/src/handlers/placement-move-push.ts +0 -478
- package/src/handlers/placement-push.sqlite.test.ts +0 -299
- package/src/handlers/probe-message-id.sqlite.test.ts +0 -192
- package/src/handlers/probe-message-id.ts +0 -15
- package/src/handlers/sync-mailboxes.test.ts +0 -183
- package/src/handlers/sync-mailboxes.ts +0 -425
- package/src/handlers/sync-message-body.sqlite.test.ts +0 -314
- package/src/handlers/sync-message-body.test.ts +0 -150
- package/src/handlers/sync-message-body.ts +0 -366
- package/src/handlers/sync-messages.sqlite.test.ts +0 -195
- package/src/handlers/sync-messages.ts +0 -537
- package/src/index.test.ts +0 -22
- package/src/index.ts +0 -82
- package/src/mail-sync-selection.sqlite.test.ts +0 -227
- package/src/mailbox-row.ts +0 -20
- package/src/pending-sync.sqlite.test.ts +0 -165
- package/src/poller.ts +0 -55
- package/src/processor.ts +0 -62
- package/src/queues.ts +0 -40
- package/src/scheduler/attachment-sweep.sqlite.test.ts +0 -74
- package/src/scheduler/attachment-sweep.ts +0 -46
- package/src/scheduler/calendar-subscriptions.sqlite.test.ts +0 -147
- package/src/scheduler/calendar-subscriptions.ts +0 -103
- package/src/scheduler/config.test.ts +0 -40
- package/src/scheduler/decide-due.test.ts +0 -44
- package/src/scheduler/half-deleted-accounts.sqlite.test.ts +0 -196
- package/src/scheduler/loop.test.ts +0 -143
- package/src/scheduler/run-tick.sqlite.test.ts +0 -191
- package/src/scheduler/run-tick.ts +0 -281
- package/src/scheduler/stranded-placement-redrive.ts +0 -265
- package/src/stranded-placement.sqlite.test.ts +0 -799
- package/src/test-helpers/test-world.ts +0 -425
- package/src/test-helpers/worker-queues.ts +0 -11
- package/src/thread-join.sqlite.test.ts +0 -106
- package/src/with-oauth-lifecycle-deps.sqlite.test.ts +0 -269
- package/src/with-oauth-lifecycle-deps.ts +0 -71
- package/src/with-oauth-lifecycle.ts +0 -158
- package/tsconfig.json +0 -8
|
@@ -1,314 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { after, afterEach, before, beforeEach, describe, it } from "node:test";
|
|
3
|
-
import { GetParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
|
|
4
|
-
import type { MailboxItem } from "@remit/data-ports";
|
|
5
|
-
import {
|
|
6
|
-
FilterClauseField,
|
|
7
|
-
FilterScope,
|
|
8
|
-
MailboxCursorState,
|
|
9
|
-
MessageStatus,
|
|
10
|
-
} from "@remit/domain-enums";
|
|
11
|
-
import { isMessageBodySyncBroken } from "@remit/mailbox-service";
|
|
12
|
-
import { mockClient } from "aws-sdk-client-mock";
|
|
13
|
-
import { resetBodySyncGateCache } from "../body-sync-gate.js";
|
|
14
|
-
import type { SyncMessageBodyEvent } from "../events.js";
|
|
15
|
-
import {
|
|
16
|
-
openStore,
|
|
17
|
-
type SeededMessage,
|
|
18
|
-
type World,
|
|
19
|
-
} from "../test-helpers/test-world.js";
|
|
20
|
-
import {
|
|
21
|
-
BODY_SYNC_MAX_ATTEMPTS,
|
|
22
|
-
syncMessageBody,
|
|
23
|
-
} from "./sync-message-body.js";
|
|
24
|
-
|
|
25
|
-
const ssm = mockClient(SSMClient);
|
|
26
|
-
|
|
27
|
-
let store: Awaited<ReturnType<typeof openStore>>;
|
|
28
|
-
|
|
29
|
-
before(async () => {
|
|
30
|
-
store = await openStore();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
after(() => store.close());
|
|
34
|
-
|
|
35
|
-
const bodySync = (enabled: boolean) =>
|
|
36
|
-
ssm
|
|
37
|
-
.on(GetParameterCommand)
|
|
38
|
-
.resolves({ Parameter: { Value: enabled ? "true" : "false" } });
|
|
39
|
-
|
|
40
|
-
beforeEach(() => {
|
|
41
|
-
ssm.reset();
|
|
42
|
-
resetBodySyncGateCache();
|
|
43
|
-
bodySync(true);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
afterEach(() => resetBodySyncGateCache());
|
|
47
|
-
|
|
48
|
-
interface Arranged {
|
|
49
|
-
w: World;
|
|
50
|
-
inbox: MailboxItem;
|
|
51
|
-
message: SeededMessage;
|
|
52
|
-
event: SyncMessageBodyEvent;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const arrange = async (): Promise<Arranged> => {
|
|
56
|
-
const w = await store.world();
|
|
57
|
-
const inbox = await w.folder("INBOX");
|
|
58
|
-
const message = await w.message(inbox);
|
|
59
|
-
return {
|
|
60
|
-
w,
|
|
61
|
-
inbox,
|
|
62
|
-
message,
|
|
63
|
-
event: {
|
|
64
|
-
type: "SYNC_MESSAGE_BODY",
|
|
65
|
-
eventId: `evt-${message.messageId}`,
|
|
66
|
-
timestamp: Date.now(),
|
|
67
|
-
accountId: w.account.accountId,
|
|
68
|
-
mailboxId: inbox.mailboxId,
|
|
69
|
-
messageIds: [message.messageId],
|
|
70
|
-
messages: [{ messageId: message.messageId, uid: message.uid }],
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const run = (a: Arranged, receiveCount = 1) =>
|
|
76
|
-
syncMessageBody(a.event, a.w.log, a.w.queues, receiveCount, a.w.scope);
|
|
77
|
-
|
|
78
|
-
const bodyFetches = (a: Arranged): string[] => a.w.imap.commands("BODY");
|
|
79
|
-
|
|
80
|
-
describe("a body sync", () => {
|
|
81
|
-
it("fetches the body and stores it against the message", async () => {
|
|
82
|
-
const a = await arrange();
|
|
83
|
-
|
|
84
|
-
await run(a);
|
|
85
|
-
|
|
86
|
-
assert.equal(bodyFetches(a).length, 1);
|
|
87
|
-
assert.ok((await a.w.row(a.message.messageId))?.bodyStorageKey);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
// Issue #1270: swallowing the failure into a fresh enqueue meant the queue's
|
|
91
|
-
// own redelivery and dead-letter queue never engaged.
|
|
92
|
-
it("throws within the budget when the server cannot return the body, so the queue redelivers", async () => {
|
|
93
|
-
const a = await arrange();
|
|
94
|
-
a.w.imap.failNext.BODY = new Error("Connection reset");
|
|
95
|
-
|
|
96
|
-
await assert.rejects(run(a), /Connection reset/);
|
|
97
|
-
|
|
98
|
-
assert.equal(
|
|
99
|
-
(await a.w.row(a.message.messageId))?.bodyStorageKey,
|
|
100
|
-
undefined,
|
|
101
|
-
);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it("fetches nothing while body sync is paused", async () => {
|
|
105
|
-
const a = await arrange();
|
|
106
|
-
ssm.reset();
|
|
107
|
-
bodySync(false);
|
|
108
|
-
|
|
109
|
-
await run(a);
|
|
110
|
-
|
|
111
|
-
assert.equal(a.w.imap.connections, 0);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("acks without connecting when the mailbox was deleted", async () => {
|
|
115
|
-
const a = await arrange();
|
|
116
|
-
await a.w.client.mailbox.delete(a.w.account.accountId, a.inbox.mailboxId);
|
|
117
|
-
|
|
118
|
-
await run(a);
|
|
119
|
-
|
|
120
|
-
assert.equal(a.w.imap.connections, 0);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it("fetches nothing while the cursor is rebuilding", async () => {
|
|
124
|
-
const a = await arrange();
|
|
125
|
-
await a.w.client.mailbox.update(a.w.account.accountId, a.inbox.mailboxId, {
|
|
126
|
-
cursorState: MailboxCursorState.rebuilding,
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
await run(a);
|
|
130
|
-
|
|
131
|
-
assert.equal(a.w.imap.connections, 0);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it("fetches nothing when the mailbox's UIDVALIDITY changed under it", async () => {
|
|
135
|
-
const a = await arrange();
|
|
136
|
-
a.w.imap.setUidValidity("INBOX", 2);
|
|
137
|
-
|
|
138
|
-
await run(a);
|
|
139
|
-
|
|
140
|
-
assert.deepEqual(bodyFetches(a), []);
|
|
141
|
-
assert.equal(
|
|
142
|
-
(await a.w.row(a.message.messageId))?.bodyStorageKey,
|
|
143
|
-
undefined,
|
|
144
|
-
);
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
const batchOf = (
|
|
149
|
-
w: World,
|
|
150
|
-
inbox: MailboxItem,
|
|
151
|
-
messages: SeededMessage[],
|
|
152
|
-
): SyncMessageBodyEvent => ({
|
|
153
|
-
type: "SYNC_MESSAGE_BODY",
|
|
154
|
-
eventId: `evt-${messages.map((m) => m.messageId).join("-")}`,
|
|
155
|
-
timestamp: Date.now(),
|
|
156
|
-
accountId: w.account.accountId,
|
|
157
|
-
mailboxId: inbox.mailboxId,
|
|
158
|
-
messageIds: messages.map((m) => m.messageId),
|
|
159
|
-
messages: messages.map((m) => ({ messageId: m.messageId, uid: m.uid })),
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
const runBatch = (w: World, event: SyncMessageBodyEvent, receiveCount = 1) =>
|
|
163
|
-
syncMessageBody(event, w.log, w.queues, receiveCount, w.scope);
|
|
164
|
-
|
|
165
|
-
const arrangeFiling = async () => {
|
|
166
|
-
const w = await store.world();
|
|
167
|
-
const inbox = await w.folder("INBOX");
|
|
168
|
-
const travel = await w.folder("Travel");
|
|
169
|
-
await w.client.filter.create({
|
|
170
|
-
accountConfigId: w.account.accountConfigId,
|
|
171
|
-
name: "From a friend",
|
|
172
|
-
scope: FilterScope.Standing,
|
|
173
|
-
literalClauses: [
|
|
174
|
-
{ field: FilterClauseField.From, value: "friend@example.com" },
|
|
175
|
-
],
|
|
176
|
-
actionMailboxId: travel.mailboxId,
|
|
177
|
-
});
|
|
178
|
-
const first = await w.message(inbox);
|
|
179
|
-
const second = await w.message(inbox);
|
|
180
|
-
return {
|
|
181
|
-
w,
|
|
182
|
-
travel,
|
|
183
|
-
first,
|
|
184
|
-
second,
|
|
185
|
-
event: batchOf(w, inbox, [first, second]),
|
|
186
|
-
};
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
describe("a body batch whose filter files mail while the queue is refusing", () => {
|
|
190
|
-
it("fails at the first filing it cannot enqueue and leaves the rest untouched", async () => {
|
|
191
|
-
const { w, first, second, event } = await arrangeFiling();
|
|
192
|
-
w.refuseSends(new Error("connect ECONNREFUSED 127.0.0.1:9324"));
|
|
193
|
-
|
|
194
|
-
await assert.rejects(runBatch(w, event), /ECONNREFUSED/);
|
|
195
|
-
|
|
196
|
-
const untouched = await w.row(second.messageId);
|
|
197
|
-
assert.equal(untouched?.status, MessageStatus.active);
|
|
198
|
-
assert.equal(untouched?.bodyStorageKey, undefined);
|
|
199
|
-
assert.equal(await w.client.placementMove.find(second.messageId), null);
|
|
200
|
-
assert.equal(
|
|
201
|
-
(await w.client.placementMove.find(first.messageId))?.state,
|
|
202
|
-
"pending",
|
|
203
|
-
);
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it("files and stores the whole batch on the redelivery once the queue accepts", async () => {
|
|
207
|
-
const { w, travel, first, second, event } = await arrangeFiling();
|
|
208
|
-
w.refuseSends(new Error("connect ECONNREFUSED 127.0.0.1:9324"));
|
|
209
|
-
await assert.rejects(runBatch(w, event), /ECONNREFUSED/);
|
|
210
|
-
|
|
211
|
-
w.refuseSends(undefined);
|
|
212
|
-
await runBatch(w, event, 2);
|
|
213
|
-
|
|
214
|
-
for (const message of [first, second]) {
|
|
215
|
-
const row = await w.row(message.messageId);
|
|
216
|
-
assert.equal(row?.mailboxId, travel.mailboxId);
|
|
217
|
-
assert.ok(row?.bodyStorageKey, `${message.messageId} body stored`);
|
|
218
|
-
assert.equal(row?.movedByRemit, true);
|
|
219
|
-
assert.equal(row?.filterMove?.sourceMailboxId, event.mailboxId);
|
|
220
|
-
assert.equal(
|
|
221
|
-
(await w.client.placementMove.find(message.messageId))?.state,
|
|
222
|
-
"queued",
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
describe("a body batch on its last attempt while the queue is refusing", () => {
|
|
229
|
-
it("throws, so the batch dead-letters instead of its messages being marked broken", async () => {
|
|
230
|
-
const { w, first: message, event } = await arrangeFiling();
|
|
231
|
-
w.refuseSends(new Error("connect ECONNREFUSED 127.0.0.1:9324"));
|
|
232
|
-
|
|
233
|
-
await assert.rejects(
|
|
234
|
-
runBatch(w, event, BODY_SYNC_MAX_ATTEMPTS),
|
|
235
|
-
/ECONNREFUSED/,
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
assert.equal(
|
|
239
|
-
await isMessageBodySyncBroken(
|
|
240
|
-
w.client.storage,
|
|
241
|
-
w.account.accountConfigId,
|
|
242
|
-
w.account.accountId,
|
|
243
|
-
message.messageId,
|
|
244
|
-
),
|
|
245
|
-
false,
|
|
246
|
-
);
|
|
247
|
-
});
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
const startMove = (
|
|
251
|
-
w: World,
|
|
252
|
-
message: SeededMessage,
|
|
253
|
-
from: MailboxItem,
|
|
254
|
-
to: MailboxItem,
|
|
255
|
-
) =>
|
|
256
|
-
w.client.message.transitionPlacement(
|
|
257
|
-
message.messageId,
|
|
258
|
-
{
|
|
259
|
-
status: MessageStatus.active,
|
|
260
|
-
mailboxId: from.mailboxId,
|
|
261
|
-
uid: message.uid,
|
|
262
|
-
},
|
|
263
|
-
{
|
|
264
|
-
mailboxId: to.mailboxId,
|
|
265
|
-
status: MessageStatus.moving,
|
|
266
|
-
originalMailboxId: from.mailboxId,
|
|
267
|
-
originalUid: message.uid,
|
|
268
|
-
},
|
|
269
|
-
);
|
|
270
|
-
|
|
271
|
-
describe("a body batch that has gone stale", () => {
|
|
272
|
-
it("does not fetch a message from a folder its confirmed move took it out of", async () => {
|
|
273
|
-
const w = await store.world();
|
|
274
|
-
const inbox = await w.folder("INBOX");
|
|
275
|
-
const travel = await w.folder("Travel");
|
|
276
|
-
const moved = await w.message(inbox);
|
|
277
|
-
const neighbour = await w.message(inbox);
|
|
278
|
-
await startMove(w, moved, inbox, travel);
|
|
279
|
-
await w.client.message.updateUid(
|
|
280
|
-
moved.messageId,
|
|
281
|
-
neighbour.uid,
|
|
282
|
-
travel.mailboxId,
|
|
283
|
-
);
|
|
284
|
-
|
|
285
|
-
await runBatch(w, batchOf(w, inbox, [moved]));
|
|
286
|
-
|
|
287
|
-
assert.deepEqual(w.imap.commands("BODY"), []);
|
|
288
|
-
assert.equal((await w.row(moved.messageId))?.bodyStorageKey, undefined);
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
it("fetches a message whose move has not confirmed from the folder its uid is in", async () => {
|
|
292
|
-
const w = await store.world();
|
|
293
|
-
const inbox = await w.folder("INBOX");
|
|
294
|
-
const travel = await w.folder("Travel");
|
|
295
|
-
const filing = await w.message(inbox);
|
|
296
|
-
await startMove(w, filing, inbox, travel);
|
|
297
|
-
|
|
298
|
-
await runBatch(w, batchOf(w, inbox, [filing]));
|
|
299
|
-
|
|
300
|
-
assert.equal(w.imap.commands("BODY").length, 1);
|
|
301
|
-
assert.ok((await w.row(filing.messageId))?.bodyStorageKey);
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
it("acks a batch whose message row has been deleted since", async () => {
|
|
305
|
-
const w = await store.world();
|
|
306
|
-
const inbox = await w.folder("INBOX");
|
|
307
|
-
const gone = await w.message(inbox);
|
|
308
|
-
await w.client.message.delete(gone.messageId);
|
|
309
|
-
|
|
310
|
-
await runBatch(w, batchOf(w, inbox, [gone]));
|
|
311
|
-
|
|
312
|
-
assert.equal(w.imap.connections, 0);
|
|
313
|
-
});
|
|
314
|
-
});
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert";
|
|
2
|
-
import { describe, test } from "node:test";
|
|
3
|
-
import type { SyncedMessage } from "@remit/mailbox-service";
|
|
4
|
-
import type { SyncMessageBodyEvent } from "../events.js";
|
|
5
|
-
import {
|
|
6
|
-
buildRetryableFailureError,
|
|
7
|
-
resolveBatch,
|
|
8
|
-
} from "./sync-message-body.js";
|
|
9
|
-
import { batchSyncedMessages } from "./sync-messages.js";
|
|
10
|
-
|
|
11
|
-
const baseEvent = {
|
|
12
|
-
type: "SYNC_MESSAGE_BODY" as const,
|
|
13
|
-
accountId: "test-account-123",
|
|
14
|
-
mailboxId: "test-mailbox-456",
|
|
15
|
-
eventId: "event-789",
|
|
16
|
-
timestamp: 1700000000000,
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
describe("resolveBatch — event-shape preference", () => {
|
|
20
|
-
test("prefers the new messages[] shape and exposes the uid map", () => {
|
|
21
|
-
const event: SyncMessageBodyEvent = {
|
|
22
|
-
...baseEvent,
|
|
23
|
-
messageIds: ["msg-1", "msg-2"],
|
|
24
|
-
messages: [
|
|
25
|
-
{ messageId: "msg-1", uid: 101 },
|
|
26
|
-
{ messageId: "msg-2", uid: 102 },
|
|
27
|
-
],
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const { messageIds, uidByMessageId } = resolveBatch(event);
|
|
31
|
-
|
|
32
|
-
assert.deepEqual(messageIds, ["msg-1", "msg-2"]);
|
|
33
|
-
assert.ok(uidByMessageId);
|
|
34
|
-
assert.equal(uidByMessageId.get("msg-1"), 101);
|
|
35
|
-
assert.equal(uidByMessageId.get("msg-2"), 102);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test("derives messageIds from messages[] when both disagree", () => {
|
|
39
|
-
// messages[] is authoritative; a stale messageIds list must not leak in.
|
|
40
|
-
const event: SyncMessageBodyEvent = {
|
|
41
|
-
...baseEvent,
|
|
42
|
-
messageIds: ["stale"],
|
|
43
|
-
messages: [{ messageId: "msg-1", uid: 101 }],
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const { messageIds } = resolveBatch(event);
|
|
47
|
-
|
|
48
|
-
assert.deepEqual(messageIds, ["msg-1"]);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test("falls back to legacy messageIds[] with no uid map", () => {
|
|
52
|
-
const event: SyncMessageBodyEvent = {
|
|
53
|
-
...baseEvent,
|
|
54
|
-
messageIds: ["msg-1", "msg-2", "msg-3"],
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const { messageIds, uidByMessageId } = resolveBatch(event);
|
|
58
|
-
|
|
59
|
-
assert.deepEqual(messageIds, ["msg-1", "msg-2", "msg-3"]);
|
|
60
|
-
assert.equal(uidByMessageId, undefined);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test("force defaults to false when the event omits it (legacy/bulk events)", () => {
|
|
64
|
-
const event: SyncMessageBodyEvent = {
|
|
65
|
-
...baseEvent,
|
|
66
|
-
messageIds: ["msg-1"],
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
assert.equal(resolveBatch(event).force, false);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("force is carried from the event's read-miss re-arm cue", () => {
|
|
73
|
-
const event: SyncMessageBodyEvent = {
|
|
74
|
-
...baseEvent,
|
|
75
|
-
messageIds: ["msg-1"],
|
|
76
|
-
messages: [{ messageId: "msg-1", uid: 101 }],
|
|
77
|
-
force: true,
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
assert.equal(resolveBatch(event).force, true);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
test("empty messages[] resolves to an empty batch, not the legacy list", () => {
|
|
84
|
-
const event: SyncMessageBodyEvent = {
|
|
85
|
-
...baseEvent,
|
|
86
|
-
messageIds: ["should-be-ignored"],
|
|
87
|
-
messages: [],
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const { messageIds, uidByMessageId } = resolveBatch(event);
|
|
91
|
-
|
|
92
|
-
assert.deepEqual(messageIds, []);
|
|
93
|
-
assert.ok(uidByMessageId);
|
|
94
|
-
assert.equal(uidByMessageId.size, 0);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
describe("buildRetryableFailureError — the DLQ-propagation signal", () => {
|
|
99
|
-
// Genuine processing failures must propagate (issue #1270): syncMessageBody
|
|
100
|
-
// throws this while SQS redelivery budget remains, instead of swallowing the
|
|
101
|
-
// failure into a fresh re-enqueue. index.ts's SQS handler catches it and
|
|
102
|
-
// reports the record as a batch item failure, so SQS redelivers it — and
|
|
103
|
-
// once the queue's own maxReceiveCount is hit, the record dead-letters into
|
|
104
|
-
// the body-dlq (alarmed in infra/stacks/dev/stacks/remit-worker-monitoring-stack.ts).
|
|
105
|
-
|
|
106
|
-
test("names every failed message id and the current attempt", () => {
|
|
107
|
-
const error = buildRetryableFailureError(["msg-1", "msg-2"], 1);
|
|
108
|
-
|
|
109
|
-
assert.match(error.message, /msg-1/);
|
|
110
|
-
assert.match(error.message, /msg-2/);
|
|
111
|
-
assert.match(error.message, /attempt 1\/3/);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
describe("batchSyncedMessages — one batch == one ranged fetch", () => {
|
|
116
|
-
const makeSynced = (count: number): SyncedMessage[] =>
|
|
117
|
-
Array.from({ length: count }, (_, i) => ({
|
|
118
|
-
messageId: `msg-${i}`,
|
|
119
|
-
uid: i + 1,
|
|
120
|
-
}));
|
|
121
|
-
|
|
122
|
-
test("packs up to 200 messages into a single batch", () => {
|
|
123
|
-
const batches = batchSyncedMessages(makeSynced(200));
|
|
124
|
-
|
|
125
|
-
assert.equal(batches.length, 1);
|
|
126
|
-
assert.equal(batches[0].length, 200);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
test("splits 201 messages into 200 + 1", () => {
|
|
130
|
-
const batches = batchSyncedMessages(makeSynced(201));
|
|
131
|
-
|
|
132
|
-
assert.equal(batches.length, 2);
|
|
133
|
-
assert.equal(batches[0].length, 200);
|
|
134
|
-
assert.equal(batches[1].length, 1);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
test("keeps messageId+uid pairs intact per batch", () => {
|
|
138
|
-
const batches = batchSyncedMessages(makeSynced(3));
|
|
139
|
-
|
|
140
|
-
assert.deepEqual(batches[0], [
|
|
141
|
-
{ messageId: "msg-0", uid: 1 },
|
|
142
|
-
{ messageId: "msg-1", uid: 2 },
|
|
143
|
-
{ messageId: "msg-2", uid: 3 },
|
|
144
|
-
]);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test("empty input yields no batches", () => {
|
|
148
|
-
assert.deepEqual(batchSyncedMessages([]), []);
|
|
149
|
-
});
|
|
150
|
-
});
|