@remit/mailbox-service 0.0.97 → 0.0.99
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 +1 -1
- package/src/body-sync-category-override.test.ts +4 -8
- package/src/body-sync-category-writeonce.test.ts +17 -17
- package/src/body-sync-placement-writeonce.test.ts +37 -22
- package/src/body-sync-resync.test.ts +605 -0
- package/src/body-sync.ts +96 -42
- package/src/mailbox-delete-intent.test.ts +246 -0
- package/src/mailbox-management.ts +91 -3
- package/src/mailbox-queue.ts +24 -8
package/package.json
CHANGED
|
@@ -381,10 +381,8 @@ describe("Address.flags.category overrides classification at sync time (issue #2
|
|
|
381
381
|
);
|
|
382
382
|
|
|
383
383
|
assert.equal(harness.messageUpdates.length, 1);
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
MessageCategory.marketing,
|
|
387
|
-
);
|
|
384
|
+
// Re-store writes only bodyStorageKey — category untouched on row + thread.
|
|
385
|
+
assert.equal(harness.messageUpdates[0].input.category, undefined);
|
|
388
386
|
assert.equal(harness.message.category, MessageCategory.marketing);
|
|
389
387
|
assert.equal(harness.rows[0].category, MessageCategory.marketing);
|
|
390
388
|
});
|
|
@@ -422,10 +420,8 @@ describe("Address.flags.category overrides classification at sync time (issue #2
|
|
|
422
420
|
|
|
423
421
|
assert.deepEqual(result.syncedMessageIds, ["m-1"]);
|
|
424
422
|
assert.equal(harness.messageUpdates.length, 1);
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
MessageCategory.marketing,
|
|
428
|
-
);
|
|
423
|
+
// Re-store writes only bodyStorageKey — category untouched on row + thread.
|
|
424
|
+
assert.equal(harness.messageUpdates[0].input.category, undefined);
|
|
429
425
|
assert.equal(harness.message.category, MessageCategory.marketing);
|
|
430
426
|
assert.equal(harness.rows[0].category, MessageCategory.marketing);
|
|
431
427
|
});
|
|
@@ -2,15 +2,13 @@
|
|
|
2
2
|
* RFC 034 Decision 3.1: `Message.category` is written once and never mutated
|
|
3
3
|
* after — RFC 030's message-list GSI sort key depends on it never churning.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* regression that drops the guard shows up even though header classification
|
|
13
|
-
* is otherwise deterministic (issue #355).
|
|
5
|
+
* The re-entrant paths — `fetchAndGetBody`'s `NoSuchKey` fallback and
|
|
6
|
+
* `syncBodies(..., force: true)` — are now body-only re-stores (issue #1011):
|
|
7
|
+
* `applyPostStoreSteps(isReStore: true)` skips the entire decision pass and
|
|
8
|
+
* writes only `bodyStorageKey`, so the existing category on the row is never
|
|
9
|
+
* touched. Each test below feeds a re-store a body that would classify
|
|
10
|
+
* differently, so a regression that drops the isReStore skip shows up as the
|
|
11
|
+
* category field appearing in the update input (or changing on the row).
|
|
14
12
|
*/
|
|
15
13
|
|
|
16
14
|
import assert from "node:assert/strict";
|
|
@@ -157,10 +155,11 @@ describe("Message.category survives a re-entrant classification pass", () => {
|
|
|
157
155
|
);
|
|
158
156
|
|
|
159
157
|
assert.equal(harness.messageUpdates.length, 1);
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
158
|
+
// Re-store writes only bodyStorageKey — category is untouched on the row.
|
|
159
|
+
assert.equal(harness.messageUpdates[0].input.category, undefined);
|
|
160
|
+
assert.deepEqual(Object.keys(harness.messageUpdates[0].input).sort(), [
|
|
161
|
+
"bodyStorageKey",
|
|
162
|
+
]);
|
|
164
163
|
assert.equal(harness.message.category, MessageCategory.marketing);
|
|
165
164
|
});
|
|
166
165
|
|
|
@@ -196,10 +195,11 @@ describe("Message.category survives a re-entrant classification pass", () => {
|
|
|
196
195
|
|
|
197
196
|
assert.deepEqual(result.syncedMessageIds, ["m-1"]);
|
|
198
197
|
assert.equal(harness.messageUpdates.length, 1);
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
198
|
+
// Re-store writes only bodyStorageKey — category is untouched on the row.
|
|
199
|
+
assert.equal(harness.messageUpdates[0].input.category, undefined);
|
|
200
|
+
assert.deepEqual(Object.keys(harness.messageUpdates[0].input).sort(), [
|
|
201
|
+
"bodyStorageKey",
|
|
202
|
+
]);
|
|
203
203
|
assert.equal(harness.message.category, MessageCategory.marketing);
|
|
204
204
|
});
|
|
205
205
|
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* RFC 039 Non-goals /
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* `
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* junked it in the first place, and can be silently moved right back.
|
|
2
|
+
* RFC 039 Non-goals / issues #383, #1011: placement is meant to run once per
|
|
3
|
+
* message. The two re-entrant paths — `fetchAndGetBody`'s `NoSuchKey` fallback
|
|
4
|
+
* and `syncBodies(..., force: true)` — are now marked as re-stores by
|
|
5
|
+
* `applyPostStoreSteps(isReStore: true)`, which skips the entire decision pass
|
|
6
|
+
* (placement, filters, classification) and writes only `bodyStorageKey`. This
|
|
7
|
+
* replaces the old per-field guards (`hasDecidedPlacement`,
|
|
8
|
+
* `hasDecidedCategory`, `hasClassifiedBody`) that each individual derivation
|
|
9
|
+
* carried.
|
|
11
10
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* heuristics happening to agree.
|
|
11
|
+
* The last test below previously asserted that a legacy row (movedByRemit: true,
|
|
12
|
+
* no placementDecidedAt) gets backfilled with placementDecidedAt on re-enter —
|
|
13
|
+
* that behavior is gone by design: re-stores never re-decide placement, so a
|
|
14
|
+
* pre-#1011 row that never got placementDecidedAt simply keeps not having it
|
|
15
|
+
* (and never gets moved by Remit either).
|
|
18
16
|
*/
|
|
19
17
|
|
|
20
18
|
import assert from "node:assert/strict";
|
|
@@ -201,6 +199,11 @@ describe("placement survives a re-entrant computePlacement pass (issue #383)", (
|
|
|
201
199
|
assert.equal(harness.message.mailboxId, MAILBOXES.inbox.mailboxId);
|
|
202
200
|
assert.equal(harness.messageUpdates[0]?.input.movedByRemit, undefined);
|
|
203
201
|
assert.equal(harness.messageUpdates[0]?.input.placementVerdict, undefined);
|
|
202
|
+
// The re-store wrote only bodyStorageKey — no placement fields.
|
|
203
|
+
assert.equal(
|
|
204
|
+
harness.messageUpdates[0]?.input.placementDecidedAt,
|
|
205
|
+
undefined,
|
|
206
|
+
);
|
|
204
207
|
});
|
|
205
208
|
|
|
206
209
|
it("keeps a user-rescued message in Inbox when syncBodies re-fetches with force", async () => {
|
|
@@ -279,16 +282,18 @@ describe("placement survives a re-entrant computePlacement pass (issue #383)", (
|
|
|
279
282
|
);
|
|
280
283
|
});
|
|
281
284
|
|
|
282
|
-
it("
|
|
285
|
+
it("a legacy row with movedByRemit: true and no placementDecidedAt is not re-decided on re-store", async () => {
|
|
283
286
|
const harness = buildHarness(
|
|
284
287
|
{
|
|
285
288
|
messageId: "m-1",
|
|
286
289
|
mailboxId: MAILBOXES.inbox.mailboxId,
|
|
287
290
|
bodyStorageKey: "s3://bodies/m-1",
|
|
288
291
|
movedByRemit: true,
|
|
289
|
-
// No placementDecidedAt — a legacy row synced before
|
|
290
|
-
//
|
|
291
|
-
//
|
|
292
|
+
// No placementDecidedAt and no category — a legacy row synced before
|
|
293
|
+
// the write-once guards existed. Under #1011 a re-store skips the
|
|
294
|
+
// decision pass entirely, so placement is never re-evaluated and the
|
|
295
|
+
// row's missing fields are never backfilled — the re-store writes only
|
|
296
|
+
// bodyStorageKey.
|
|
292
297
|
},
|
|
293
298
|
async () => {
|
|
294
299
|
throw noSuchKeyError();
|
|
@@ -313,10 +318,20 @@ describe("placement survives a re-entrant computePlacement pass (issue #383)", (
|
|
|
313
318
|
[],
|
|
314
319
|
"movedByRemit must keep protecting a legacy row with no placementDecidedAt of its own",
|
|
315
320
|
);
|
|
321
|
+
// The re-store wrote only bodyStorageKey — no placement fields were
|
|
322
|
+
// re-decided or backfilled.
|
|
316
323
|
assert.equal(harness.message.mailboxId, MAILBOXES.inbox.mailboxId);
|
|
317
|
-
assert.
|
|
318
|
-
|
|
319
|
-
|
|
324
|
+
assert.equal(
|
|
325
|
+
harness.messageUpdates[0]?.input.placementDecidedAt,
|
|
326
|
+
undefined,
|
|
327
|
+
);
|
|
328
|
+
assert.equal(harness.messageUpdates[0]?.input.movedByRemit, undefined);
|
|
329
|
+
assert.equal(harness.messageUpdates[0]?.input.placementVerdict, undefined);
|
|
330
|
+
// The only field written is bodyStorageKey.
|
|
331
|
+
assert.equal(
|
|
332
|
+
Object.keys(harness.messageUpdates[0]?.input ?? {}).length,
|
|
333
|
+
1,
|
|
334
|
+
"re-store must write only bodyStorageKey",
|
|
320
335
|
);
|
|
321
336
|
});
|
|
322
337
|
});
|