@remit/drizzle-service 0.0.64 → 0.0.66
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 +2 -1
- package/src/index.ts +2 -0
- package/src/repair/junk-only-address.sqlite.test.ts +221 -72
- package/src/repair/junk-only-address.ts +116 -68
- package/src/repos/config-import-atomicity.sqlite.test.ts +255 -0
- package/src/repos/i4-account-setting.ts +23 -1
- package/src/repos/i4-address-junk-move.sqlite.test.ts +119 -15
- package/src/repos/i4-address.ts +15 -7
- package/src/repos/i4-config-import.ts +94 -0
- package/src/repos/i4-mailbox-special-use.ts +161 -1
- package/src/schema/i4-config-import.ts +3 -0
- package/src/schema.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remit/drizzle-service",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.66",
|
|
4
4
|
"description": "Drizzle ORM service over SQLite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"fix": "biome check --write src"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
+
"@remit/config-transfer": "*",
|
|
21
22
|
"drizzle-kit": "^0.31.1",
|
|
22
23
|
"tsx": "*"
|
|
23
24
|
},
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ export * from "./repos/i4-account-config.js";
|
|
|
15
15
|
export * from "./repos/i4-account-export-request.js";
|
|
16
16
|
export * from "./repos/i4-account-setting.js";
|
|
17
17
|
export * from "./repos/i4-address.js";
|
|
18
|
+
export * from "./repos/i4-config-import.js";
|
|
18
19
|
export * from "./repos/i4-mailbox.js";
|
|
19
20
|
export * from "./repos/i4-mailbox-lock.js";
|
|
20
21
|
export * from "./repos/i4-mailbox-special-use.js";
|
|
@@ -49,6 +50,7 @@ export * from "./schema/i4-account-config.js";
|
|
|
49
50
|
export * from "./schema/i4-account-export-request.js";
|
|
50
51
|
export * from "./schema/i4-account-setting.js";
|
|
51
52
|
export * from "./schema/i4-address.js";
|
|
53
|
+
export * from "./schema/i4-config-import.js";
|
|
52
54
|
export * from "./schema/i4-mailbox.js";
|
|
53
55
|
export * from "./schema/i4-mailbox-lock.js";
|
|
54
56
|
export * from "./schema/i4-message-flag-push.js";
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { after, before, beforeEach, describe, test } from "node:test";
|
|
4
|
+
import { composeFolderRoleAppointmentName } from "@remit/data-ports/folder-role";
|
|
5
|
+
import { CanonicalMailboxRole } from "@remit/domain-enums";
|
|
4
6
|
import Database from "better-sqlite3";
|
|
7
|
+
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
8
|
+
import { AccountSettingRepo } from "../repos/i4-account-setting.js";
|
|
9
|
+
import { MailboxSpecialUseRepo } from "../repos/i4-mailbox-special-use.js";
|
|
5
10
|
import { shippedTableDdl } from "../test-shipped-sqlite-schema.js";
|
|
6
11
|
import {
|
|
7
12
|
type JunkOnlyRepairClient,
|
|
13
|
+
type JunkOnlyRepairMode,
|
|
14
|
+
type JunkOnlyReport,
|
|
8
15
|
sweepJunkOnlyAddresses,
|
|
9
16
|
} from "./junk-only-address.js";
|
|
10
17
|
|
|
@@ -22,8 +29,47 @@ interface AddressRow {
|
|
|
22
29
|
|
|
23
30
|
describe("addresses standing only on mail in Junk", () => {
|
|
24
31
|
let sqlite: Database.Database;
|
|
32
|
+
let specialUse: MailboxSpecialUseRepo;
|
|
33
|
+
let accountSetting: AccountSettingRepo;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Which folder holds Junk is resolved through the repository seam every
|
|
37
|
+
* other special-folder lookup reads, so the sweep honours an appointment and
|
|
38
|
+
* cannot disagree with the sync that harvested the address.
|
|
39
|
+
*/
|
|
40
|
+
const sweep = async (
|
|
41
|
+
mode: JunkOnlyRepairMode = "repair",
|
|
42
|
+
): Promise<JunkOnlyReport> =>
|
|
43
|
+
sweepJunkOnlyAddresses(
|
|
44
|
+
clientOver(sqlite),
|
|
45
|
+
mode,
|
|
46
|
+
await specialUse.resolveJunkRolesForInstance(),
|
|
47
|
+
);
|
|
25
48
|
|
|
26
|
-
const
|
|
49
|
+
const account = (accountId: string): void => {
|
|
50
|
+
sqlite
|
|
51
|
+
.prepare(
|
|
52
|
+
`INSERT OR IGNORE INTO account (
|
|
53
|
+
account_id, account_config_id, username, email, imap_host,
|
|
54
|
+
imap_port, imap_tls, imap_start_tls, smtp_port, is_active,
|
|
55
|
+
connection_state, created_at, updated_at
|
|
56
|
+
) VALUES (?, 'cfg-1', 'user', 'user@example.com', 'imap.example.com',
|
|
57
|
+
993, 1, 0, 465, 1, 'disconnected', 0, 0)`,
|
|
58
|
+
)
|
|
59
|
+
.run(accountId);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const mailbox = (
|
|
63
|
+
mailboxId: string,
|
|
64
|
+
designation: string | null,
|
|
65
|
+
folder: {
|
|
66
|
+
accountId?: string;
|
|
67
|
+
fullPath?: string;
|
|
68
|
+
hierarchyDelimiter?: string;
|
|
69
|
+
} = {},
|
|
70
|
+
): void => {
|
|
71
|
+
const accountId = folder.accountId ?? "acc";
|
|
72
|
+
account(accountId);
|
|
27
73
|
sqlite
|
|
28
74
|
.prepare(
|
|
29
75
|
`INSERT INTO mailbox (
|
|
@@ -32,9 +78,29 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
32
78
|
unseen_count, deleted_count, total_size, last_sync_uid,
|
|
33
79
|
high_water_mark_uid, last_message_sync_at, special_use,
|
|
34
80
|
created_at, updated_at
|
|
35
|
-
) VALUES (?,
|
|
81
|
+
) VALUES (?, ?, '', ?, ?, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, ?, 0, 0)`,
|
|
36
82
|
)
|
|
37
|
-
.run(
|
|
83
|
+
.run(
|
|
84
|
+
mailboxId,
|
|
85
|
+
accountId,
|
|
86
|
+
folder.hierarchyDelimiter ?? "/",
|
|
87
|
+
folder.fullPath ?? mailboxId,
|
|
88
|
+
designation,
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const appointJunk = async (
|
|
93
|
+
accountId: string,
|
|
94
|
+
mailboxId: string,
|
|
95
|
+
): Promise<void> => {
|
|
96
|
+
await accountSetting.upsert({
|
|
97
|
+
accountConfigId: "cfg-1",
|
|
98
|
+
name: composeFolderRoleAppointmentName(
|
|
99
|
+
accountId,
|
|
100
|
+
CanonicalMailboxRole.Junk,
|
|
101
|
+
),
|
|
102
|
+
value: { kind: "String", value: mailboxId },
|
|
103
|
+
});
|
|
38
104
|
};
|
|
39
105
|
|
|
40
106
|
const specialUseEntry = (mailboxId: string, specialUse: string): void => {
|
|
@@ -117,12 +183,17 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
117
183
|
|
|
118
184
|
before(() => {
|
|
119
185
|
sqlite = new Database(":memory:");
|
|
186
|
+
const db = drizzle(sqlite);
|
|
187
|
+
specialUse = new MailboxSpecialUseRepo(db as never);
|
|
188
|
+
accountSetting = new AccountSettingRepo(db as never);
|
|
120
189
|
for (const table of [
|
|
121
190
|
"address",
|
|
122
191
|
"envelope_address",
|
|
123
192
|
"message",
|
|
124
193
|
"mailbox",
|
|
125
194
|
"mailbox_special_use_entry",
|
|
195
|
+
"account_setting",
|
|
196
|
+
"account",
|
|
126
197
|
]) {
|
|
127
198
|
sqlite.exec(shippedTableDdl(DDL_TAG, table));
|
|
128
199
|
}
|
|
@@ -148,6 +219,8 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
148
219
|
"message",
|
|
149
220
|
"mailbox",
|
|
150
221
|
"mailbox_special_use_entry",
|
|
222
|
+
"account_setting",
|
|
223
|
+
"account",
|
|
151
224
|
]) {
|
|
152
225
|
sqlite.exec(`DELETE FROM ${table}`);
|
|
153
226
|
}
|
|
@@ -167,7 +240,7 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
167
240
|
sighting("spammer", "spam-1");
|
|
168
241
|
sighting("spammer", "spam-2");
|
|
169
242
|
|
|
170
|
-
const report = await
|
|
243
|
+
const report = await sweep();
|
|
171
244
|
|
|
172
245
|
assert.equal(report.withholdable, 1);
|
|
173
246
|
assert.equal(report.withheld, 1);
|
|
@@ -181,7 +254,7 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
181
254
|
sighting("colleague", "spam-1");
|
|
182
255
|
sighting("colleague", "mail-1");
|
|
183
256
|
|
|
184
|
-
const report = await
|
|
257
|
+
const report = await sweep();
|
|
185
258
|
|
|
186
259
|
assert.equal(report.withholdable, 0);
|
|
187
260
|
assert.equal(withheld("colleague"), false);
|
|
@@ -194,7 +267,7 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
194
267
|
.prepare("UPDATE message SET mailbox_id = 'junk' WHERE message_id = ?")
|
|
195
268
|
.run("mail-1");
|
|
196
269
|
|
|
197
|
-
await
|
|
270
|
+
await sweep();
|
|
198
271
|
|
|
199
272
|
assert.equal(withheld("newsletter"), true);
|
|
200
273
|
});
|
|
@@ -202,13 +275,13 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
202
275
|
test("restores an address whose message moved out of Junk", async () => {
|
|
203
276
|
address("misfiled");
|
|
204
277
|
sighting("misfiled", "spam-1");
|
|
205
|
-
await
|
|
278
|
+
await sweep();
|
|
206
279
|
assert.equal(withheld("misfiled"), true);
|
|
207
280
|
|
|
208
281
|
sqlite
|
|
209
282
|
.prepare("UPDATE message SET mailbox_id = 'inbox' WHERE message_id = ?")
|
|
210
283
|
.run("spam-1");
|
|
211
|
-
const report = await
|
|
284
|
+
const report = await sweep();
|
|
212
285
|
|
|
213
286
|
assert.equal(report.restorable, 1);
|
|
214
287
|
assert.equal(report.restored, 1);
|
|
@@ -219,7 +292,7 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
219
292
|
address("client", { outbound: 1 });
|
|
220
293
|
sighting("client", "spam-1");
|
|
221
294
|
|
|
222
|
-
const report = await
|
|
295
|
+
const report = await sweep();
|
|
223
296
|
|
|
224
297
|
assert.equal(report.withholdable, 0);
|
|
225
298
|
assert.equal(withheld("client"), false);
|
|
@@ -229,7 +302,7 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
229
302
|
address("friend", { reply: 2 });
|
|
230
303
|
sighting("friend", "spam-1");
|
|
231
304
|
|
|
232
|
-
await
|
|
305
|
+
await sweep();
|
|
233
306
|
|
|
234
307
|
assert.equal(withheld("friend"), false);
|
|
235
308
|
});
|
|
@@ -238,7 +311,7 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
238
311
|
address("boss", {}, '{"vip":{"value":true,"setAt":1}}');
|
|
239
312
|
sighting("boss", "spam-1");
|
|
240
313
|
|
|
241
|
-
await
|
|
314
|
+
await sweep();
|
|
242
315
|
|
|
243
316
|
assert.equal(withheld("boss"), false);
|
|
244
317
|
});
|
|
@@ -247,13 +320,13 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
247
320
|
address("reformed", {}, '{"junkOnly":{"value":true,"setAt":1}}');
|
|
248
321
|
sighting("reformed", "spam-1");
|
|
249
322
|
|
|
250
|
-
await
|
|
323
|
+
await sweep();
|
|
251
324
|
assert.equal(withheld("reformed"), true);
|
|
252
325
|
|
|
253
326
|
sqlite
|
|
254
327
|
.prepare("UPDATE address SET outbound_count = 1 WHERE address_id = ?")
|
|
255
328
|
.run("reformed");
|
|
256
|
-
await
|
|
329
|
+
await sweep();
|
|
257
330
|
|
|
258
331
|
assert.equal(withheld("reformed"), false);
|
|
259
332
|
});
|
|
@@ -262,7 +335,7 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
262
335
|
address("ex-colleague");
|
|
263
336
|
sighting("ex-colleague", "bin-1");
|
|
264
337
|
|
|
265
|
-
const report = await
|
|
338
|
+
const report = await sweep();
|
|
266
339
|
|
|
267
340
|
assert.equal(report.withholdable, 0);
|
|
268
341
|
assert.equal(withheld("ex-colleague"), false);
|
|
@@ -273,7 +346,7 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
273
346
|
sighting("spammer", "spam-1");
|
|
274
347
|
sighting("spammer", "bin-1");
|
|
275
348
|
|
|
276
|
-
await
|
|
349
|
+
await sweep();
|
|
277
350
|
|
|
278
351
|
assert.equal(withheld("spammer"), true);
|
|
279
352
|
});
|
|
@@ -282,14 +355,14 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
282
355
|
address("junk-then-bin");
|
|
283
356
|
address("bin-then-junk");
|
|
284
357
|
sighting("junk-then-bin", "spam-1");
|
|
285
|
-
await
|
|
358
|
+
await sweep();
|
|
286
359
|
sighting("junk-then-bin", "bin-1");
|
|
287
|
-
await
|
|
360
|
+
await sweep();
|
|
288
361
|
|
|
289
362
|
sighting("bin-then-junk", "bin-1");
|
|
290
|
-
await
|
|
363
|
+
await sweep();
|
|
291
364
|
sighting("bin-then-junk", "spam-1");
|
|
292
|
-
await
|
|
365
|
+
await sweep();
|
|
293
366
|
|
|
294
367
|
assert.equal(withheld("junk-then-bin"), true);
|
|
295
368
|
assert.equal(withheld("bin-then-junk"), true);
|
|
@@ -299,12 +372,12 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
299
372
|
address("spammer");
|
|
300
373
|
sighting("spammer", "spam-1");
|
|
301
374
|
sighting("spammer", "bin-1");
|
|
302
|
-
await
|
|
375
|
+
await sweep();
|
|
303
376
|
|
|
304
377
|
sqlite
|
|
305
378
|
.prepare("UPDATE address SET flags = '{}' WHERE address_id = ?")
|
|
306
379
|
.run("spammer");
|
|
307
|
-
await
|
|
380
|
+
await sweep();
|
|
308
381
|
|
|
309
382
|
assert.equal(withheld("spammer"), true);
|
|
310
383
|
});
|
|
@@ -312,12 +385,12 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
312
385
|
test("deleting every spam message leaves the mark standing", async () => {
|
|
313
386
|
address("spammer");
|
|
314
387
|
sighting("spammer", "spam-1");
|
|
315
|
-
await
|
|
388
|
+
await sweep();
|
|
316
389
|
|
|
317
390
|
sqlite
|
|
318
391
|
.prepare("UPDATE message SET mailbox_id = 'trash' WHERE message_id = ?")
|
|
319
392
|
.run("spam-1");
|
|
320
|
-
const report = await
|
|
393
|
+
const report = await sweep();
|
|
321
394
|
|
|
322
395
|
assert.equal(report.restorable, 0);
|
|
323
396
|
assert.equal(withheld("spammer"), true);
|
|
@@ -326,12 +399,12 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
326
399
|
test("deleting the spam does not put its sender back", async () => {
|
|
327
400
|
address("spammer");
|
|
328
401
|
sighting("spammer", "spam-1");
|
|
329
|
-
await
|
|
402
|
+
await sweep();
|
|
330
403
|
|
|
331
404
|
sqlite
|
|
332
405
|
.prepare("UPDATE message SET mailbox_id = 'trash' WHERE message_id = ?")
|
|
333
406
|
.run("spam-1");
|
|
334
|
-
const report = await
|
|
407
|
+
const report = await sweep();
|
|
335
408
|
|
|
336
409
|
assert.equal(report.restorable, 0);
|
|
337
410
|
assert.equal(withheld("spammer"), true);
|
|
@@ -340,10 +413,10 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
340
413
|
test("purging the spam does not put its sender back", async () => {
|
|
341
414
|
address("spammer");
|
|
342
415
|
sighting("spammer", "spam-1");
|
|
343
|
-
await
|
|
416
|
+
await sweep();
|
|
344
417
|
|
|
345
418
|
sqlite.prepare("DELETE FROM message WHERE message_id = ?").run("spam-1");
|
|
346
|
-
const report = await
|
|
419
|
+
const report = await sweep();
|
|
347
420
|
|
|
348
421
|
assert.equal(report.restorable, 0);
|
|
349
422
|
assert.equal(withheld("spammer"), true);
|
|
@@ -355,7 +428,7 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
355
428
|
sighting("reported", "spam-1");
|
|
356
429
|
sighting("hushed", "spam-1");
|
|
357
430
|
|
|
358
|
-
await
|
|
431
|
+
await sweep();
|
|
359
432
|
|
|
360
433
|
assert.equal(withheld("reported"), true);
|
|
361
434
|
assert.equal(withheld("hushed"), true);
|
|
@@ -364,7 +437,7 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
364
437
|
test("blocking a withheld sender never lifts the mark", async () => {
|
|
365
438
|
address("spammer");
|
|
366
439
|
sighting("spammer", "spam-1");
|
|
367
|
-
await
|
|
440
|
+
await sweep();
|
|
368
441
|
|
|
369
442
|
sqlite
|
|
370
443
|
.prepare(
|
|
@@ -373,96 +446,172 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
373
446
|
WHERE address_id = ?`,
|
|
374
447
|
)
|
|
375
448
|
.run("spammer");
|
|
376
|
-
const report = await
|
|
449
|
+
const report = await sweep();
|
|
377
450
|
|
|
378
451
|
assert.equal(report.restorable, 0);
|
|
379
452
|
assert.equal(withheld("spammer"), true);
|
|
380
453
|
});
|
|
381
454
|
|
|
382
455
|
test("reads a Junk folder a server does not designate", async () => {
|
|
383
|
-
mailbox("named-spam", null);
|
|
456
|
+
mailbox("named-spam", null, { accountId: "acc-named", fullPath: "Spam" });
|
|
384
457
|
message("spam-5", "named-spam");
|
|
385
458
|
address("by-name");
|
|
386
459
|
sighting("by-name", "spam-5");
|
|
387
|
-
sqlite
|
|
388
|
-
.prepare("UPDATE mailbox SET full_path = 'Spam' WHERE mailbox_id = ?")
|
|
389
|
-
.run("named-spam");
|
|
390
460
|
|
|
391
|
-
await
|
|
461
|
+
await sweep();
|
|
392
462
|
|
|
393
463
|
assert.equal(withheld("by-name"), true);
|
|
394
464
|
});
|
|
395
465
|
|
|
396
466
|
test("reads a Junk folder nested under any prefix", async () => {
|
|
397
|
-
mailbox("nested-spam", null
|
|
467
|
+
mailbox("nested-spam", null, {
|
|
468
|
+
accountId: "acc-nested",
|
|
469
|
+
fullPath: "INBOX/Spam",
|
|
470
|
+
});
|
|
398
471
|
message("spam-6", "nested-spam");
|
|
399
472
|
address("nested");
|
|
400
473
|
sighting("nested", "spam-6");
|
|
401
|
-
sqlite
|
|
402
|
-
.prepare(
|
|
403
|
-
"UPDATE mailbox SET full_path = 'INBOX/Spam' WHERE mailbox_id = ?",
|
|
404
|
-
)
|
|
405
|
-
.run("nested-spam");
|
|
406
474
|
|
|
407
|
-
await
|
|
475
|
+
await sweep();
|
|
408
476
|
|
|
409
477
|
assert.equal(withheld("nested"), true);
|
|
410
478
|
});
|
|
411
479
|
|
|
412
480
|
test("reads a Junk folder under a delimiter that is not a slash", async () => {
|
|
413
|
-
mailbox("dotted-spam", null
|
|
481
|
+
mailbox("dotted-spam", null, {
|
|
482
|
+
accountId: "acc-dotted",
|
|
483
|
+
fullPath: "Mail.Junk E-mail",
|
|
484
|
+
hierarchyDelimiter: ".",
|
|
485
|
+
});
|
|
414
486
|
message("spam-7", "dotted-spam");
|
|
415
487
|
address("dotted");
|
|
416
488
|
sighting("dotted", "spam-7");
|
|
417
|
-
sqlite
|
|
418
|
-
.prepare(
|
|
419
|
-
`UPDATE mailbox SET full_path = 'Mail.Junk E-mail',
|
|
420
|
-
hierarchy_delimiter = '.' WHERE mailbox_id = ?`,
|
|
421
|
-
)
|
|
422
|
-
.run("dotted-spam");
|
|
423
489
|
|
|
424
|
-
await
|
|
490
|
+
await sweep();
|
|
425
491
|
|
|
426
492
|
assert.equal(withheld("dotted"), true);
|
|
427
493
|
});
|
|
428
494
|
|
|
429
495
|
test("never reads a prefix as the folder it names", async () => {
|
|
430
|
-
mailbox("spam-parent", null
|
|
496
|
+
mailbox("spam-parent", null, {
|
|
497
|
+
accountId: "acc-parent",
|
|
498
|
+
fullPath: "Spam/Receipts",
|
|
499
|
+
});
|
|
431
500
|
message("mail-2", "spam-parent");
|
|
432
501
|
address("under-spam");
|
|
433
502
|
sighting("under-spam", "mail-2");
|
|
434
|
-
sqlite
|
|
435
|
-
.prepare(
|
|
436
|
-
"UPDATE mailbox SET full_path = 'Spam/Receipts' WHERE mailbox_id = ?",
|
|
437
|
-
)
|
|
438
|
-
.run("spam-parent");
|
|
439
503
|
|
|
440
|
-
await
|
|
504
|
+
await sweep();
|
|
441
505
|
|
|
442
506
|
assert.equal(withheld("under-spam"), false);
|
|
443
507
|
});
|
|
444
508
|
|
|
445
|
-
test("reads the designation
|
|
446
|
-
mailbox("
|
|
447
|
-
|
|
509
|
+
test("reads the designation the mailbox sync stored", async () => {
|
|
510
|
+
mailbox("entry-only", null, {
|
|
511
|
+
accountId: "acc-entry",
|
|
512
|
+
fullPath: "Bulk",
|
|
513
|
+
});
|
|
448
514
|
specialUseEntry("entry-only", "Junk");
|
|
449
|
-
message("spam-3", "column-only");
|
|
450
515
|
message("spam-4", "entry-only");
|
|
451
|
-
address("by-column");
|
|
452
516
|
address("by-entry");
|
|
453
|
-
sighting("by-column", "spam-3");
|
|
454
517
|
sighting("by-entry", "spam-4");
|
|
455
518
|
|
|
456
|
-
await
|
|
519
|
+
await sweep();
|
|
457
520
|
|
|
458
|
-
assert.equal(withheld("by-column"), true);
|
|
459
521
|
assert.equal(withheld("by-entry"), true);
|
|
460
522
|
});
|
|
461
523
|
|
|
524
|
+
test("a second folder named Spam never claims the role", async () => {
|
|
525
|
+
mailbox("second-spam", null, { fullPath: "Archive/Spam" });
|
|
526
|
+
message("spam-3", "second-spam");
|
|
527
|
+
address("filed-away");
|
|
528
|
+
sighting("filed-away", "spam-3");
|
|
529
|
+
|
|
530
|
+
await sweep();
|
|
531
|
+
|
|
532
|
+
assert.equal(withheld("filed-away"), false);
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
test("withholds on the Junk folder the account appointed", async () => {
|
|
536
|
+
mailbox("rubbish", null, {
|
|
537
|
+
accountId: "acc-appointed",
|
|
538
|
+
fullPath: "INBOX/Rubbish",
|
|
539
|
+
});
|
|
540
|
+
await appointJunk("acc-appointed", "rubbish");
|
|
541
|
+
message("spam-8", "rubbish");
|
|
542
|
+
address("appointed-spammer");
|
|
543
|
+
sighting("appointed-spammer", "spam-8");
|
|
544
|
+
|
|
545
|
+
await sweep();
|
|
546
|
+
|
|
547
|
+
assert.equal(withheld("appointed-spammer"), true);
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
test("leaves the same folder alone when nobody appointed it", async () => {
|
|
551
|
+
mailbox("unclaimed-rubbish", null, {
|
|
552
|
+
accountId: "acc-unappointed",
|
|
553
|
+
fullPath: "INBOX/Rubbish",
|
|
554
|
+
});
|
|
555
|
+
message("spam-9", "unclaimed-rubbish");
|
|
556
|
+
address("stranger");
|
|
557
|
+
sighting("stranger", "spam-9");
|
|
558
|
+
|
|
559
|
+
await sweep();
|
|
560
|
+
|
|
561
|
+
assert.equal(withheld("stranger"), false);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* No folder holds Junk anywhere, so nothing is known about any sighting. The
|
|
566
|
+
* predicate must be silent in both directions: an empty id list read as "no
|
|
567
|
+
* message is in Junk" would make every sighting live mail and hand back
|
|
568
|
+
* every mark the instance had earned.
|
|
569
|
+
*/
|
|
570
|
+
test("an instance with no Junk folder neither withholds nor restores", async () => {
|
|
571
|
+
for (const table of [
|
|
572
|
+
"envelope_address",
|
|
573
|
+
"message",
|
|
574
|
+
"mailbox",
|
|
575
|
+
"mailbox_special_use_entry",
|
|
576
|
+
]) {
|
|
577
|
+
sqlite.exec(`DELETE FROM ${table}`);
|
|
578
|
+
}
|
|
579
|
+
mailbox("plain-inbox", null, { fullPath: "INBOX" });
|
|
580
|
+
message("mail-9", "plain-inbox");
|
|
581
|
+
address("colleague");
|
|
582
|
+
address("misfiled", {}, '{"junkOnly":{"value":true,"setAt":1}}');
|
|
583
|
+
sighting("colleague", "mail-9");
|
|
584
|
+
sighting("misfiled", "mail-9");
|
|
585
|
+
|
|
586
|
+
const report = await sweep();
|
|
587
|
+
|
|
588
|
+
assert.equal(report.withholdable, 0);
|
|
589
|
+
assert.equal(report.restorable, 0);
|
|
590
|
+
assert.equal(withheld("colleague"), false);
|
|
591
|
+
assert.equal(withheld("misfiled"), true);
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
test("standing the account gave an address lifts its mark regardless", async () => {
|
|
595
|
+
for (const table of ["mailbox", "mailbox_special_use_entry"]) {
|
|
596
|
+
sqlite.exec(`DELETE FROM ${table}`);
|
|
597
|
+
}
|
|
598
|
+
mailbox("plain-inbox", null, { fullPath: "INBOX" });
|
|
599
|
+
address(
|
|
600
|
+
"reformed",
|
|
601
|
+
{ outbound: 1 },
|
|
602
|
+
'{"junkOnly":{"value":true,"setAt":1}}',
|
|
603
|
+
);
|
|
604
|
+
|
|
605
|
+
const report = await sweep();
|
|
606
|
+
|
|
607
|
+
assert.equal(report.restorable, 1);
|
|
608
|
+
assert.equal(withheld("reformed"), false);
|
|
609
|
+
});
|
|
610
|
+
|
|
462
611
|
test("leaves an address no message has ever carried alone", async () => {
|
|
463
612
|
address("orphan");
|
|
464
613
|
|
|
465
|
-
const report = await
|
|
614
|
+
const report = await sweep();
|
|
466
615
|
|
|
467
616
|
assert.equal(report.withholdable, 0);
|
|
468
617
|
assert.equal(withheld("orphan"), false);
|
|
@@ -476,7 +625,7 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
476
625
|
sighting("colleague", "mail-1");
|
|
477
626
|
sighting("client", "spam-2");
|
|
478
627
|
|
|
479
|
-
await
|
|
628
|
+
await sweep();
|
|
480
629
|
|
|
481
630
|
assert.equal(addressCount(), 3);
|
|
482
631
|
});
|
|
@@ -485,7 +634,7 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
485
634
|
address("noisy", {}, '{"muted":{"value":true,"setAt":7}}');
|
|
486
635
|
sighting("noisy", "spam-1");
|
|
487
636
|
|
|
488
|
-
await
|
|
637
|
+
await sweep();
|
|
489
638
|
|
|
490
639
|
assert.deepEqual(JSON.parse(read("noisy").flags).muted, {
|
|
491
640
|
value: true,
|
|
@@ -496,10 +645,10 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
496
645
|
test("a second run writes nothing", async () => {
|
|
497
646
|
address("spammer");
|
|
498
647
|
sighting("spammer", "spam-1");
|
|
499
|
-
await
|
|
648
|
+
await sweep();
|
|
500
649
|
const first = read("spammer");
|
|
501
650
|
|
|
502
|
-
const report = await
|
|
651
|
+
const report = await sweep();
|
|
503
652
|
|
|
504
653
|
assert.equal(report.withholdable, 0);
|
|
505
654
|
assert.equal(report.restorable, 0);
|
|
@@ -514,7 +663,7 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
514
663
|
address("misfiled", {}, '{"junkOnly":{"value":true,"setAt":1}}');
|
|
515
664
|
sighting("misfiled", "mail-1");
|
|
516
665
|
|
|
517
|
-
const report = await
|
|
666
|
+
const report = await sweep("check");
|
|
518
667
|
|
|
519
668
|
assert.equal(report.withholdable, 1);
|
|
520
669
|
assert.equal(report.restorable, 1);
|
|
@@ -528,7 +677,7 @@ describe("addresses standing only on mail in Junk", () => {
|
|
|
528
677
|
address("legacy", {}, "");
|
|
529
678
|
sighting("legacy", "spam-1");
|
|
530
679
|
|
|
531
|
-
await
|
|
680
|
+
await sweep();
|
|
532
681
|
|
|
533
682
|
assert.equal(withheld("legacy"), true);
|
|
534
683
|
});
|