@remit/mailbox-service 0.0.111 → 0.0.113
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-credentials.d.ts +83 -0
- package/dist/account-credentials.js +104 -0
- package/dist/attribute-mapper.d.ts +36 -0
- package/dist/attribute-mapper.js +119 -0
- package/dist/body-parse.d.ts +28 -0
- package/{src/body-parse.ts → dist/body-parse.js} +18 -26
- package/dist/body-part-mapper.d.ts +92 -0
- package/dist/body-part-mapper.js +301 -0
- package/dist/body-sync-queue.d.ts +59 -0
- package/dist/body-sync-queue.js +56 -0
- package/dist/body-sync-terminal.d.ts +58 -0
- package/dist/body-sync-terminal.js +95 -0
- package/dist/body-sync.d.ts +351 -0
- package/dist/body-sync.js +1204 -0
- package/dist/calendar-parts.d.ts +23 -0
- package/dist/calendar-parts.js +18 -0
- package/dist/classification-backfill.d.ts +91 -0
- package/dist/classification-backfill.js +180 -0
- package/dist/classify-message.d.ts +31 -0
- package/dist/classify-message.js +89 -0
- package/dist/connection-factory.d.ts +54 -0
- package/dist/connection-factory.js +54 -0
- package/dist/connection-test.d.ts +41 -0
- package/dist/connection-test.js +152 -0
- package/dist/external-move.d.ts +94 -0
- package/dist/external-move.js +173 -0
- package/dist/filter-operations.d.ts +42 -0
- package/dist/filter-operations.js +211 -0
- package/dist/filters/filter-config.d.ts +14 -0
- package/dist/filters/filter-config.js +10 -0
- package/{src/filters/list-id.ts → dist/filters/list-id.d.ts} +2 -16
- package/dist/filters/list-id.js +26 -0
- package/dist/filters/match.d.ts +69 -0
- package/dist/filters/match.js +165 -0
- package/dist/filters/pipeline.d.ts +64 -0
- package/dist/filters/pipeline.js +100 -0
- package/{src/flag-push-terminal.ts → dist/flag-push-terminal.d.ts} +19 -77
- package/dist/flag-push-terminal.js +63 -0
- package/dist/flag-push.d.ts +69 -0
- package/dist/flag-push.js +111 -0
- package/dist/flag-queue.d.ts +175 -0
- package/dist/flag-queue.js +291 -0
- package/dist/folder-operations.d.ts +39 -0
- package/dist/folder-operations.js +112 -0
- package/dist/folder-placement.d.ts +13 -0
- package/dist/folder-placement.js +23 -0
- package/dist/heuristics/authenticationResults.d.ts +10 -0
- package/dist/heuristics/authenticationResults.js +187 -0
- package/dist/heuristics/classifyByHeaders.d.ts +82 -0
- package/dist/heuristics/classifyByHeaders.js +365 -0
- package/dist/heuristics/classifyPlacement.d.ts +67 -0
- package/dist/heuristics/classifyPlacement.js +153 -0
- package/dist/heuristics/machineSenders.d.ts +21 -0
- package/{src/heuristics/machineSenders.ts → dist/heuristics/machineSenders.js} +34 -40
- package/dist/heuristics/senderMismatch.d.ts +81 -0
- package/dist/heuristics/senderMismatch.js +192 -0
- package/dist/heuristics/socialDomains.d.ts +14 -0
- package/{src/heuristics/socialDomains.ts → dist/heuristics/socialDomains.js} +9 -9
- package/dist/heuristics/transactionalDomains.d.ts +18 -0
- package/{src/heuristics/transactionalDomains.ts → dist/heuristics/transactionalDomains.js} +8 -8
- package/dist/imapflow-connection.d.ts +377 -0
- package/dist/imapflow-connection.js +1147 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +65 -0
- package/dist/list-id-backfill.d.ts +83 -0
- package/dist/list-id-backfill.js +132 -0
- package/dist/mailbox-cursor-rebuild.d.ts +74 -0
- package/dist/mailbox-cursor-rebuild.js +71 -0
- package/{src/mailbox-cursor.ts → dist/mailbox-cursor.d.ts} +20 -98
- package/dist/mailbox-cursor.js +102 -0
- package/dist/mailbox-intent.d.ts +41 -0
- package/{src/mailbox-intent.ts → dist/mailbox-intent.js} +39 -63
- package/dist/mailbox-management.d.ts +240 -0
- package/dist/mailbox-management.js +576 -0
- package/dist/mailbox-presence.d.ts +52 -0
- package/{src/mailbox-presence.ts → dist/mailbox-presence.js} +19 -30
- package/dist/mailbox-queue.d.ts +126 -0
- package/dist/mailbox-queue.js +214 -0
- package/dist/mailbox-sync.d.ts +93 -0
- package/dist/mailbox-sync.js +490 -0
- package/dist/mailbox-upstream.d.ts +38 -0
- package/{src/mailbox-upstream.ts → dist/mailbox-upstream.js} +37 -47
- package/dist/message-category.d.ts +51 -0
- package/{src/message-category.ts → dist/message-category.js} +28 -71
- package/dist/message-move.d.ts +285 -0
- package/dist/message-move.js +858 -0
- package/dist/message-parser.d.ts +14 -0
- package/dist/message-parser.js +17 -0
- package/{src/message-presence.ts → dist/message-presence.d.ts} +1 -13
- package/dist/message-presence.js +29 -0
- package/dist/message-read.d.ts +51 -0
- package/dist/message-read.js +31 -0
- package/dist/message-sync.d.ts +345 -0
- package/dist/message-sync.js +1358 -0
- package/dist/mime-walker.d.ts +102 -0
- package/dist/mime-walker.js +198 -0
- package/dist/organize-job.d.ts +20 -0
- package/dist/organize-job.js +37 -0
- package/dist/outbox-attachment-content.d.ts +37 -0
- package/dist/outbox-attachment-content.js +50 -0
- package/dist/outbox-attachment-filename.d.ts +19 -0
- package/dist/outbox-attachment-filename.js +64 -0
- package/dist/outbox-attachment.d.ts +148 -0
- package/dist/outbox-attachment.js +273 -0
- package/dist/outbox-queue.d.ts +73 -0
- package/dist/outbox-queue.js +284 -0
- package/{src/outbox-status.ts → dist/outbox-status.d.ts} +1 -6
- package/dist/outbox-status.js +18 -0
- package/dist/ownership.d.ts +24 -0
- package/dist/ownership.js +83 -0
- package/dist/placement-move-terminal.d.ts +67 -0
- package/dist/placement-move-terminal.js +88 -0
- package/dist/placement-move.d.ts +83 -0
- package/dist/placement-move.js +216 -0
- package/{src/placement-settled.ts → dist/placement-settled.d.ts} +19 -50
- package/dist/placement-settled.js +75 -0
- package/dist/quarantine.d.ts +95 -0
- package/dist/quarantine.js +131 -0
- package/dist/refusal.d.ts +31 -0
- package/dist/refusal.js +3 -0
- package/{src/restore-source-placement.ts → dist/restore-source-placement.d.ts} +29 -101
- package/dist/restore-source-placement.js +79 -0
- package/dist/search-text-backfill.d.ts +15 -0
- package/dist/search-text-backfill.js +33 -0
- package/dist/snippet.d.ts +61 -0
- package/dist/snippet.js +129 -0
- package/dist/spam-report.d.ts +86 -0
- package/dist/spam-report.js +241 -0
- package/dist/sync-warning.d.ts +21 -0
- package/dist/sync-warning.js +26 -0
- package/dist/sync-watermarks.d.ts +138 -0
- package/dist/sync-watermarks.js +205 -0
- package/dist/test-helpers/fake-imap.d.ts +151 -0
- package/dist/test-helpers/fake-imap.js +508 -0
- package/dist/test-helpers/folder-roles.d.ts +34 -0
- package/dist/test-helpers/folder-roles.js +47 -0
- package/dist/test-helpers/mime-tree-generator.d.ts +82 -0
- package/dist/test-helpers/mime-tree-generator.js +329 -0
- package/dist/test-helpers/test-store.d.ts +69 -0
- package/dist/test-helpers/test-store.js +193 -0
- package/dist/text/normalizer.d.ts +20 -0
- package/dist/text/normalizer.js +102 -0
- package/dist/thread-message-rows.d.ts +69 -0
- package/dist/thread-message-rows.js +40 -0
- package/dist/thread-root.d.ts +7 -0
- package/dist/thread-root.js +25 -0
- package/dist/types.d.ts +343 -0
- package/dist/types.js +19 -0
- package/package.json +86 -73
- package/src/account-credentials.ts +0 -210
- package/src/adversarial-mime.e2e.test.ts +0 -307
- package/src/attribute-mapper.test.ts +0 -104
- package/src/attribute-mapper.ts +0 -148
- package/src/body-parse.test.ts +0 -55
- package/src/body-part-mapper.property.test.ts +0 -354
- package/src/body-part-mapper.ts +0 -360
- package/src/body-sync-auto-read-writeonce.test.ts +0 -219
- package/src/body-sync-calendar-suggestion.test.ts +0 -433
- package/src/body-sync-category-denormalize.test.ts +0 -352
- package/src/body-sync-category-override.test.ts +0 -501
- package/src/body-sync-category-writeonce.test.ts +0 -234
- package/src/body-sync-filter.test.ts +0 -237
- package/src/body-sync-message-gone.test.ts +0 -139
- package/src/body-sync-placement-flags.test.ts +0 -347
- package/src/body-sync-placement-writeonce.test.ts +0 -337
- package/src/body-sync-quarantine.test.ts +0 -276
- package/src/body-sync-queue.test.ts +0 -124
- package/src/body-sync-queue.ts +0 -131
- package/src/body-sync-resync.test.ts +0 -605
- package/src/body-sync-terminal.test.ts +0 -310
- package/src/body-sync-terminal.ts +0 -176
- package/src/body-sync-unclassifiable-once.test.ts +0 -260
- package/src/body-sync-unsubscribed-auto-read.test.ts +0 -176
- package/src/body-sync.ts +0 -1970
- package/src/calendar-parts.ts +0 -41
- package/src/classification-backfill.test.ts +0 -615
- package/src/classification-backfill.ts +0 -363
- package/src/classify-message.test.ts +0 -205
- package/src/classify-message.ts +0 -116
- package/src/connection-factory.ts +0 -102
- package/src/connection-test.ts +0 -208
- package/src/credentials.test.ts +0 -181
- package/src/external-move.test.ts +0 -369
- package/src/external-move.ts +0 -272
- package/src/filters/anchor-drift.ts +0 -55
- package/src/filters/filter-config.test.ts +0 -229
- package/src/filters/filter-config.ts +0 -69
- package/src/filters/list-id.test.ts +0 -50
- package/src/filters/match.test.ts +0 -429
- package/src/filters/match.ts +0 -274
- package/src/filters/message-embedder.test.ts +0 -39
- package/src/filters/message-embedder.ts +0 -33
- package/src/filters/pipeline.test.ts +0 -330
- package/src/filters/pipeline.ts +0 -284
- package/src/flag-push-terminal.test.ts +0 -209
- package/src/flag-push.test.ts +0 -408
- package/src/flag-push.ts +0 -198
- package/src/flag-queue-stars.test.ts +0 -171
- package/src/flag-queue.ts +0 -509
- package/src/folder-placement.ts +0 -56
- package/src/heuristics/authenticationResults.test.ts +0 -189
- package/src/heuristics/authenticationResults.ts +0 -212
- package/src/heuristics/classifyByHeaders.test.ts +0 -889
- package/src/heuristics/classifyByHeaders.ts +0 -467
- package/src/heuristics/classifyPlacement.test.ts +0 -366
- package/src/heuristics/classifyPlacement.ts +0 -211
- package/src/heuristics/classifyRealMail.test.ts +0 -242
- package/src/heuristics/machineSenders.test.ts +0 -101
- package/src/heuristics/senderMismatch.test.ts +0 -346
- package/src/heuristics/senderMismatch.ts +0 -259
- package/src/imapflow-connect-retry.test.ts +0 -134
- package/src/imapflow-connection.e2e.test.ts +0 -335
- package/src/imapflow-connection.integ.test.ts +0 -316
- package/src/imapflow-connection.test.ts +0 -469
- package/src/imapflow-connection.ts +0 -1475
- package/src/index.ts +0 -350
- package/src/list-id-backfill.test.ts +0 -573
- package/src/list-id-backfill.ts +0 -274
- package/src/mailbox-cursor-rebuild.test.ts +0 -171
- package/src/mailbox-cursor-rebuild.ts +0 -146
- package/src/mailbox-cursor.test.ts +0 -249
- package/src/mailbox-delete-intent.test.ts +0 -246
- package/src/mailbox-management.test.ts +0 -356
- package/src/mailbox-management.ts +0 -839
- package/src/mailbox-presence.test.ts +0 -72
- package/src/mailbox-queue.ts +0 -415
- package/src/mailbox-rename-intent.test.ts +0 -389
- package/src/mailbox-rename-subtree.test.ts +0 -664
- package/src/mailbox-sync.test.ts +0 -848
- package/src/mailbox-sync.ts +0 -724
- package/src/message-delete-junk-reconcile.test.ts +0 -117
- package/src/message-delete-no-trash.test.ts +0 -245
- package/src/message-delete-unsettled-placement.test.ts +0 -396
- package/src/message-move-copy.test.ts +0 -562
- package/src/message-move-empty-trash.test.ts +0 -326
- package/src/message-move-same-mailbox.test.ts +0 -139
- package/src/message-move-unsettled-placement.test.ts +0 -378
- package/src/message-move.ts +0 -1348
- package/src/message-parser.ts +0 -26
- package/src/message-presence.test.ts +0 -50
- package/src/message-sync-address-spoof.test.ts +0 -293
- package/src/message-sync-category.test.ts +0 -142
- package/src/message-sync-changedsince.test.ts +0 -840
- package/src/message-sync-flags.test.ts +0 -158
- package/src/message-sync-junk-harvest.test.ts +0 -244
- package/src/message-sync-quarantine.test.ts +0 -416
- package/src/message-sync-repoint.test.ts +0 -419
- package/src/message-sync.ts +0 -1963
- package/src/mime-walker.test.ts +0 -311
- package/src/mime-walker.ts +0 -305
- package/src/outbox-attachment-content.test.ts +0 -153
- package/src/outbox-attachment-content.ts +0 -104
- package/src/outbox-attachment-filename.test.ts +0 -132
- package/src/outbox-attachment-filename.ts +0 -76
- package/src/outbox-attachment.test.ts +0 -738
- package/src/outbox-attachment.ts +0 -536
- package/src/outbox-failed-recovery.test.ts +0 -370
- package/src/outbox-queue-settled-row.test.ts +0 -140
- package/src/outbox-queue.test.ts +0 -291
- package/src/outbox-queue.ts +0 -508
- package/src/pass-through-unit-of-work.ts +0 -17
- package/src/placement-move-terminal.test.ts +0 -300
- package/src/placement-move-terminal.ts +0 -148
- package/src/placement-move-unsettled.test.ts +0 -275
- package/src/placement-move.ts +0 -336
- package/src/placement-settled.test.ts +0 -147
- package/src/placement-transition-race.test.ts +0 -482
- package/src/quarantine.test.ts +0 -191
- package/src/quarantine.ts +0 -228
- package/src/snippet.test.ts +0 -213
- package/src/snippet.ts +0 -152
- package/src/spam-report.test.ts +0 -801
- package/src/spam-report.ts +0 -357
- package/src/stale-message-reconcile.test.ts +0 -82
- package/src/stale-message-reconcile.ts +0 -55
- package/src/sync-watermarks.test.ts +0 -409
- package/src/sync-watermarks.ts +0 -319
- package/src/system-flag-wire-format.test.ts +0 -77
- package/src/test-helpers/folder-roles.ts +0 -66
- package/src/test-helpers/isolated-mailbox.ts +0 -44
- package/src/test-helpers/mailfuzz-connection.ts +0 -27
- package/src/test-helpers/mime-tree-generator.ts +0 -461
- package/src/text/normalizer.test.ts +0 -223
- package/src/text/normalizer.ts +0 -140
- package/src/thread-message-rows.ts +0 -77
- package/src/types/mailparser-augment.d.ts +0 -15
- package/src/types/natural-porter-stemmer.d.ts +0 -50
- package/src/types/stopword.d.ts +0 -69
- package/src/types.ts +0 -371
- package/test/fixtures/mime-adversarial-e2e/01-octet-stream-pdf.eml +0 -21
- package/test/fixtures/mime-adversarial-e2e/02-nested-related-cid-image.eml +0 -31
- package/test/fixtures/mime-adversarial-e2e/03-calendar-invite.eml +0 -33
- package/test/fixtures/mime-adversarial-e2e/04-forwarded-rfc822-with-attachments.eml +0 -42
- package/test/fixtures/mime-adversarial-e2e/05-tnef-winmail.eml +0 -25
- package/test/fixtures/mime-adversarial-e2e/06-8bit-non-ascii.eml +0 -15
- package/test/fixtures/mime-adversarial-e2e/07-quoted-printable-soft-breaks.eml +0 -17
- package/test/fixtures/mime-adversarial-e2e/08-alternative-empty-text.eml +0 -18
- package/tsconfig.json +0 -8
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `thread_message.category` is the copy the list read path is about to filter
|
|
3
|
-
* on, and its write-path defects only appear under conditions a happy-path test
|
|
4
|
-
* never creates (issue #320).
|
|
5
|
-
*
|
|
6
|
-
* 1. The classifying pass wrote the Message before the ThreadMessage while the
|
|
7
|
-
* skip guard keyed off the Message. A failure between the two left the guard
|
|
8
|
-
* satisfied and the denormalized row stranded at `uncategorized` forever,
|
|
9
|
-
* because the requeued retry returned early.
|
|
10
|
-
* 2. `denormalizeCategory` resolved one arbitrary row per message through an
|
|
11
|
-
* unordered `.limit(1)`. More than one row per messageId is schema-legal —
|
|
12
|
-
* reachable through thread-root drift, not through a second mailbox, whose ids
|
|
13
|
-
* collapse to one row — and every row a message has must end up correct.
|
|
14
|
-
* 3. It also wrote only `category` on one of its paths, so the snippet and the
|
|
15
|
-
* `listId` derived from the same bytes were dropped on the floor.
|
|
16
|
-
*
|
|
17
|
-
* None of these show up on a single row in a single successful pass.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import assert from "node:assert/strict";
|
|
21
|
-
import { Readable } from "node:stream";
|
|
22
|
-
import { describe, it } from "node:test";
|
|
23
|
-
import type {
|
|
24
|
-
IAddressRepository,
|
|
25
|
-
IEnvelopeRepository,
|
|
26
|
-
IMessageRepository,
|
|
27
|
-
IThreadMessageRepository,
|
|
28
|
-
MessageItem,
|
|
29
|
-
ThreadMessageItem,
|
|
30
|
-
UpdateMessageInput,
|
|
31
|
-
UpdateThreadMessageInput,
|
|
32
|
-
} from "@remit/data-ports";
|
|
33
|
-
import { NotFoundError } from "@remit/data-ports/errors";
|
|
34
|
-
import { MessageCategory } from "@remit/domain-enums";
|
|
35
|
-
import type { StorageService } from "@remit/storage-service";
|
|
36
|
-
import { BodySyncService } from "./body-sync.js";
|
|
37
|
-
import type { IImapConnection } from "./types.js";
|
|
38
|
-
|
|
39
|
-
const LINKEDIN_EML = Buffer.from(
|
|
40
|
-
[
|
|
41
|
-
"From: LinkedIn <messages-noreply@linkedin.com>",
|
|
42
|
-
"To: me@example.com",
|
|
43
|
-
"Subject: You have a new invitation",
|
|
44
|
-
"List-Id: LinkedIn Invitations <invitations.linkedin.com>",
|
|
45
|
-
"List-Unsubscribe: <https://www.linkedin.com/e/unsub>",
|
|
46
|
-
"Content-Type: text/plain",
|
|
47
|
-
"",
|
|
48
|
-
"invitation body text",
|
|
49
|
-
].join("\r\n"),
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
type Row = Pick<
|
|
53
|
-
ThreadMessageItem,
|
|
54
|
-
| "threadMessageId"
|
|
55
|
-
| "messageId"
|
|
56
|
-
| "accountConfigId"
|
|
57
|
-
| "mailboxId"
|
|
58
|
-
| "sentDate"
|
|
59
|
-
| "isRead"
|
|
60
|
-
| "isDeleted"
|
|
61
|
-
| "hasStars"
|
|
62
|
-
| "hasAttachment"
|
|
63
|
-
| "category"
|
|
64
|
-
> &
|
|
65
|
-
Partial<Pick<ThreadMessageItem, "snippet" | "listId">>;
|
|
66
|
-
|
|
67
|
-
interface ThreadUpdate {
|
|
68
|
-
threadMessageId: string;
|
|
69
|
-
input: UpdateThreadMessageInput;
|
|
70
|
-
composites: Record<string, unknown> | undefined;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
interface Harness {
|
|
74
|
-
service: BodySyncService;
|
|
75
|
-
message: MessageItem;
|
|
76
|
-
rows: Row[];
|
|
77
|
-
messageUpdates: Array<{ messageId: string; input: UpdateMessageInput }>;
|
|
78
|
-
threadUpdates: ThreadUpdate[];
|
|
79
|
-
writeOrder: string[];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const buildRow = (
|
|
83
|
-
overrides: Partial<Row> & Pick<Row, "threadMessageId">,
|
|
84
|
-
): Row =>
|
|
85
|
-
({
|
|
86
|
-
messageId: "m-1",
|
|
87
|
-
accountConfigId: "cfg-1",
|
|
88
|
-
mailboxId: "mb-inbox",
|
|
89
|
-
sentDate: 1,
|
|
90
|
-
isRead: false,
|
|
91
|
-
isDeleted: false,
|
|
92
|
-
hasStars: false,
|
|
93
|
-
hasAttachment: false,
|
|
94
|
-
category: MessageCategory.uncategorized,
|
|
95
|
-
...overrides,
|
|
96
|
-
}) as Row;
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* The Message and ThreadMessage fixtures are mutated by their writes, so the
|
|
100
|
-
* skip guard sees the state a requeued delivery would. A harness that only
|
|
101
|
-
* records the write cannot show a stranded row: the strand is the guard reading
|
|
102
|
-
* a value that outlived a failed pass.
|
|
103
|
-
*/
|
|
104
|
-
const buildHarness = (
|
|
105
|
-
rows: Row[],
|
|
106
|
-
options: {
|
|
107
|
-
failThreadUpdates?: number;
|
|
108
|
-
} = {},
|
|
109
|
-
): Harness => {
|
|
110
|
-
const messageUpdates: Array<{
|
|
111
|
-
messageId: string;
|
|
112
|
-
input: UpdateMessageInput;
|
|
113
|
-
}> = [];
|
|
114
|
-
const threadUpdates: ThreadUpdate[] = [];
|
|
115
|
-
const writeOrder: string[] = [];
|
|
116
|
-
let threadUpdateAttempts = 0;
|
|
117
|
-
|
|
118
|
-
const message = {
|
|
119
|
-
messageId: "m-1",
|
|
120
|
-
mailboxId: "mb-inbox",
|
|
121
|
-
uid: 1,
|
|
122
|
-
category: MessageCategory.uncategorized,
|
|
123
|
-
} as unknown as MessageItem;
|
|
124
|
-
|
|
125
|
-
const messageService = {
|
|
126
|
-
get: async () => message,
|
|
127
|
-
update: async (messageId: string, input: UpdateMessageInput) => {
|
|
128
|
-
writeOrder.push("message");
|
|
129
|
-
messageUpdates.push({ messageId, input });
|
|
130
|
-
Object.assign(message, input);
|
|
131
|
-
},
|
|
132
|
-
} as unknown as IMessageRepository;
|
|
133
|
-
|
|
134
|
-
const threadMessageService = {
|
|
135
|
-
findAllByMessageId: async () => rows,
|
|
136
|
-
// Load-bearing, not a leftover: the current code never calls it, but
|
|
137
|
-
// reverting body-sync.ts to reproduce these failures does. Deleting it
|
|
138
|
-
// makes the fail-before run throw instead of demonstrating the defect.
|
|
139
|
-
getByMessageId: async () => rows[0],
|
|
140
|
-
update: async (
|
|
141
|
-
_accountConfigId: string,
|
|
142
|
-
threadMessageId: string,
|
|
143
|
-
input: UpdateThreadMessageInput,
|
|
144
|
-
updateOptions?: { composites?: Record<string, unknown> },
|
|
145
|
-
) => {
|
|
146
|
-
threadUpdateAttempts++;
|
|
147
|
-
if (threadUpdateAttempts <= (options.failThreadUpdates ?? 0)) {
|
|
148
|
-
throw new Error("thread_message write failed");
|
|
149
|
-
}
|
|
150
|
-
writeOrder.push("thread");
|
|
151
|
-
threadUpdates.push({
|
|
152
|
-
threadMessageId,
|
|
153
|
-
input,
|
|
154
|
-
composites: updateOptions?.composites,
|
|
155
|
-
});
|
|
156
|
-
const row = rows.find((r) => r.threadMessageId === threadMessageId);
|
|
157
|
-
if (row) Object.assign(row, input);
|
|
158
|
-
},
|
|
159
|
-
} as unknown as IThreadMessageRepository;
|
|
160
|
-
|
|
161
|
-
const storageService = {
|
|
162
|
-
retrieve: async () => LINKEDIN_EML,
|
|
163
|
-
storeMessageBody: async () => ({ uri: "s3://bodies/m-1" }),
|
|
164
|
-
storeMessageBodyStream: async () => ({ uri: "s3://bodies/m-1" }),
|
|
165
|
-
storeParsedBody: async () => {},
|
|
166
|
-
listBodyParts: async () => [],
|
|
167
|
-
} as unknown as StorageService;
|
|
168
|
-
|
|
169
|
-
const service = new BodySyncService(
|
|
170
|
-
messageService,
|
|
171
|
-
storageService,
|
|
172
|
-
threadMessageService,
|
|
173
|
-
{
|
|
174
|
-
getAddress: async () => {
|
|
175
|
-
throw new NotFoundError("Address not found");
|
|
176
|
-
},
|
|
177
|
-
incrementInboundCount: async () => {},
|
|
178
|
-
} as unknown as IAddressRepository,
|
|
179
|
-
{ listBodyParts: async () => [] } as unknown as IEnvelopeRepository,
|
|
180
|
-
{ info: () => {}, error: () => {} },
|
|
181
|
-
);
|
|
182
|
-
|
|
183
|
-
return {
|
|
184
|
-
service,
|
|
185
|
-
message,
|
|
186
|
-
rows,
|
|
187
|
-
messageUpdates,
|
|
188
|
-
threadUpdates,
|
|
189
|
-
writeOrder,
|
|
190
|
-
};
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
const syncBatch = (harness: Harness) =>
|
|
194
|
-
harness.service.syncBodies(
|
|
195
|
-
["m-1"],
|
|
196
|
-
"acc-1",
|
|
197
|
-
"cfg-1",
|
|
198
|
-
{ fullPath: "INBOX", placement: "inbox" },
|
|
199
|
-
async () =>
|
|
200
|
-
({
|
|
201
|
-
openBox: async () => {},
|
|
202
|
-
async *fetchMessageBodies(uids: number[]) {
|
|
203
|
-
for (const uid of uids) {
|
|
204
|
-
yield { uid, source: Readable.from([LINKEDIN_EML]) };
|
|
205
|
-
}
|
|
206
|
-
},
|
|
207
|
-
}) as unknown as IImapConnection,
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
const syncFromImap = (harness: Harness) =>
|
|
211
|
-
harness.service.fetchAndGetBody(
|
|
212
|
-
"m-1",
|
|
213
|
-
"acc-1",
|
|
214
|
-
"cfg-1",
|
|
215
|
-
{ fullPath: "INBOX", placement: "inbox" },
|
|
216
|
-
async () =>
|
|
217
|
-
({
|
|
218
|
-
openBox: async () => {},
|
|
219
|
-
fetchMessageBody: async () => LINKEDIN_EML,
|
|
220
|
-
}) as unknown as IImapConnection,
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
describe("the classifying pass survives an interrupted write", () => {
|
|
224
|
-
it("leaves the Message unclassified when the ThreadMessage write fails, so the retry runs again", async () => {
|
|
225
|
-
const harness = buildHarness([buildRow({ threadMessageId: "tm-1" })], {
|
|
226
|
-
failThreadUpdates: 1,
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
const failed = await syncBatch(harness);
|
|
230
|
-
|
|
231
|
-
assert.deepEqual(failed.failedMessageIds, ["m-1"]);
|
|
232
|
-
assert.equal(harness.message.category, MessageCategory.uncategorized);
|
|
233
|
-
assert.deepEqual(harness.messageUpdates, []);
|
|
234
|
-
|
|
235
|
-
const retried = await syncBatch(harness);
|
|
236
|
-
|
|
237
|
-
assert.deepEqual(retried.failedMessageIds, []);
|
|
238
|
-
assert.equal(harness.message.category, MessageCategory.social);
|
|
239
|
-
assert.equal(harness.rows[0].category, MessageCategory.social);
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it("writes the Message only after the denormalized row is durable", async () => {
|
|
243
|
-
const harness = buildHarness([buildRow({ threadMessageId: "tm-1" })]);
|
|
244
|
-
|
|
245
|
-
await syncBatch(harness);
|
|
246
|
-
|
|
247
|
-
assert.deepEqual(harness.writeOrder, ["thread", "message"]);
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
describe("denormalization reaches every row a message has", () => {
|
|
252
|
-
it("carries the classified category onto all of a message's rows", async () => {
|
|
253
|
-
const harness = buildHarness([
|
|
254
|
-
buildRow({ threadMessageId: "tm-inbox", mailboxId: "mb-inbox" }),
|
|
255
|
-
buildRow({
|
|
256
|
-
threadMessageId: "tm-archive",
|
|
257
|
-
mailboxId: "mb-archive",
|
|
258
|
-
isRead: true,
|
|
259
|
-
}),
|
|
260
|
-
]);
|
|
261
|
-
|
|
262
|
-
await syncBatch(harness);
|
|
263
|
-
|
|
264
|
-
assert.deepEqual(
|
|
265
|
-
harness.rows.map((r) => r.category),
|
|
266
|
-
[MessageCategory.social, MessageCategory.social],
|
|
267
|
-
);
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it("builds the composite block from each row rather than reusing the first", async () => {
|
|
271
|
-
const harness = buildHarness([
|
|
272
|
-
buildRow({ threadMessageId: "tm-inbox", mailboxId: "mb-inbox" }),
|
|
273
|
-
buildRow({
|
|
274
|
-
threadMessageId: "tm-archive",
|
|
275
|
-
mailboxId: "mb-archive",
|
|
276
|
-
isRead: true,
|
|
277
|
-
}),
|
|
278
|
-
]);
|
|
279
|
-
|
|
280
|
-
await syncBatch(harness);
|
|
281
|
-
|
|
282
|
-
assert.deepEqual(
|
|
283
|
-
harness.threadUpdates.map((u) => ({
|
|
284
|
-
threadMessageId: u.threadMessageId,
|
|
285
|
-
mailboxId: u.composites?.mailboxId,
|
|
286
|
-
isRead: u.composites?.isRead,
|
|
287
|
-
})),
|
|
288
|
-
[
|
|
289
|
-
{ threadMessageId: "tm-inbox", mailboxId: "mb-inbox", isRead: false },
|
|
290
|
-
{
|
|
291
|
-
threadMessageId: "tm-archive",
|
|
292
|
-
mailboxId: "mb-archive",
|
|
293
|
-
isRead: true,
|
|
294
|
-
},
|
|
295
|
-
],
|
|
296
|
-
);
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
it("fans the snippet and the list id out to every row on the read path", async () => {
|
|
300
|
-
// A snippet and a List-Id belong to the message, not to the mailbox a row
|
|
301
|
-
// happens to name, so every row carries them.
|
|
302
|
-
const harness = buildHarness([
|
|
303
|
-
buildRow({ threadMessageId: "tm-inbox", mailboxId: "mb-inbox" }),
|
|
304
|
-
buildRow({ threadMessageId: "tm-archive", mailboxId: "mb-archive" }),
|
|
305
|
-
]);
|
|
306
|
-
|
|
307
|
-
await syncFromImap(harness);
|
|
308
|
-
|
|
309
|
-
for (const row of harness.rows) {
|
|
310
|
-
assert.equal(row.category, MessageCategory.social);
|
|
311
|
-
assert.equal(row.listId, "invitations.linkedin.com");
|
|
312
|
-
assert.equal(row.snippet, "invitation body text");
|
|
313
|
-
}
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
it("writes nothing for a row that already carries every value", async () => {
|
|
317
|
-
const harness = buildHarness([
|
|
318
|
-
buildRow({
|
|
319
|
-
threadMessageId: "tm-inbox",
|
|
320
|
-
mailboxId: "mb-inbox",
|
|
321
|
-
category: MessageCategory.social,
|
|
322
|
-
snippet: "invitation body text",
|
|
323
|
-
listId: "invitations.linkedin.com",
|
|
324
|
-
}),
|
|
325
|
-
buildRow({ threadMessageId: "tm-archive", mailboxId: "mb-archive" }),
|
|
326
|
-
]);
|
|
327
|
-
|
|
328
|
-
await syncBatch(harness);
|
|
329
|
-
|
|
330
|
-
assert.deepEqual(
|
|
331
|
-
harness.threadUpdates.map((u) => u.threadMessageId),
|
|
332
|
-
["tm-archive"],
|
|
333
|
-
);
|
|
334
|
-
});
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
describe("the classifying pass writes the whole denormalized set", () => {
|
|
338
|
-
// Nothing but this pass ever derives `listId` from a message's bytes, so a
|
|
339
|
-
// write that carried only `category` left the row's `list_id` NULL for good.
|
|
340
|
-
it("carries the list id and the snippet, not just the category", async () => {
|
|
341
|
-
const harness = buildHarness([buildRow({ threadMessageId: "tm-1" })]);
|
|
342
|
-
|
|
343
|
-
await syncBatch(harness);
|
|
344
|
-
|
|
345
|
-
assert.deepEqual(harness.threadUpdates.length, 1);
|
|
346
|
-
assert.deepEqual(harness.threadUpdates[0].input, {
|
|
347
|
-
category: MessageCategory.social,
|
|
348
|
-
snippet: "invitation body text",
|
|
349
|
-
listId: "invitations.linkedin.com",
|
|
350
|
-
});
|
|
351
|
-
});
|
|
352
|
-
});
|