@remit/mailbox-service 0.0.23 → 0.0.24
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/filters/pipeline.test.ts +101 -0
package/package.json
CHANGED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The index-time filter pass over an anchorless literal filter — the shape the
|
|
3
|
+
* organize widen creates on a deployment without the vector pipeline: `From`
|
|
4
|
+
* clauses combined with `Or`, `hasAnchor: false`. This is what keeps a standing
|
|
5
|
+
* "move all mail from these senders" filter working on future mail with no
|
|
6
|
+
* vectors, so it is pinned directly: a matching sender resolves the move, a
|
|
7
|
+
* non-matching one resolves nothing, and the embedder is never touched.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import { describe, it } from "node:test";
|
|
12
|
+
import type {
|
|
13
|
+
FilterItem,
|
|
14
|
+
IFilterAnchorRepository,
|
|
15
|
+
IFilterRepository,
|
|
16
|
+
IMessageLabelRepository,
|
|
17
|
+
} from "@remit/data-ports";
|
|
18
|
+
import {
|
|
19
|
+
FilterClauseField,
|
|
20
|
+
FilterMatchOperator,
|
|
21
|
+
FilterState,
|
|
22
|
+
} from "@remit/domain-enums";
|
|
23
|
+
import type { PlacementMoveService } from "../placement-move.js";
|
|
24
|
+
import { NO_ACTION } from "./match.js";
|
|
25
|
+
import { type FilterConfig, FilterPipeline } from "./pipeline.js";
|
|
26
|
+
|
|
27
|
+
const senderFilter = (destinationMailboxId: string): FilterItem =>
|
|
28
|
+
({
|
|
29
|
+
filterId: "flt-senders",
|
|
30
|
+
accountConfigId: "cfg-1",
|
|
31
|
+
name: "npm barrage",
|
|
32
|
+
scope: "Standing",
|
|
33
|
+
state: FilterState.Active,
|
|
34
|
+
hasAnchor: false,
|
|
35
|
+
ruleChangedAt: 1,
|
|
36
|
+
matchOperator: FilterMatchOperator.Or,
|
|
37
|
+
literalClauses: [
|
|
38
|
+
{ field: FilterClauseField.From, value: "npm@github.com" },
|
|
39
|
+
{ field: FilterClauseField.From, value: "notifications@github.com" },
|
|
40
|
+
],
|
|
41
|
+
actionLabelId: NO_ACTION,
|
|
42
|
+
actionMailboxId: destinationMailboxId,
|
|
43
|
+
createdAt: 1,
|
|
44
|
+
updatedAt: 1,
|
|
45
|
+
}) as unknown as FilterItem;
|
|
46
|
+
|
|
47
|
+
const buildPipeline = (filter: FilterItem) => {
|
|
48
|
+
const state = { embedCalls: 0 };
|
|
49
|
+
const config: FilterConfig = {
|
|
50
|
+
filterService: {
|
|
51
|
+
listByAccountAndState: async () => [filter],
|
|
52
|
+
refreshExpiry: async (f: FilterItem) => f,
|
|
53
|
+
} as unknown as IFilterRepository,
|
|
54
|
+
filterAnchorService: {
|
|
55
|
+
get: async () => undefined,
|
|
56
|
+
} as unknown as IFilterAnchorRepository,
|
|
57
|
+
messageLabelService: {} as unknown as IMessageLabelRepository,
|
|
58
|
+
placementMoveService: {} as unknown as PlacementMoveService,
|
|
59
|
+
embedder: {
|
|
60
|
+
embed: async () => {
|
|
61
|
+
state.embedCalls += 1;
|
|
62
|
+
return [];
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
return { pipeline: new FilterPipeline(config, { info: () => {} }), state };
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
describe("FilterPipeline — anchorless From/Or filter at index time", () => {
|
|
70
|
+
it("resolves the move for a matching sender and never embeds", async () => {
|
|
71
|
+
const { pipeline, state } = buildPipeline(senderFilter("mbx-archive"));
|
|
72
|
+
const decision = await pipeline.evaluate("cfg-1", "m-1", {
|
|
73
|
+
from: "npm@github.com",
|
|
74
|
+
fromName: "npm",
|
|
75
|
+
subject: "A new version of left-pad is available",
|
|
76
|
+
text: "body",
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
assert.deepEqual(decision.move, {
|
|
80
|
+
destinationMailboxId: "mbx-archive",
|
|
81
|
+
filterId: "flt-senders",
|
|
82
|
+
});
|
|
83
|
+
// A literal-only filter is decided by its clauses alone — the embedder is
|
|
84
|
+
// never constructed or called.
|
|
85
|
+
assert.equal(state.embedCalls, 0);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("resolves nothing for a sender none of the clauses match", async () => {
|
|
89
|
+
const { pipeline, state } = buildPipeline(senderFilter("mbx-archive"));
|
|
90
|
+
const decision = await pipeline.evaluate("cfg-1", "m-2", {
|
|
91
|
+
from: "hello@stripe.com",
|
|
92
|
+
fromName: "Stripe",
|
|
93
|
+
subject: "Your receipt",
|
|
94
|
+
text: "body",
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
assert.equal(decision.move, undefined);
|
|
98
|
+
assert.deepEqual(decision.labels, []);
|
|
99
|
+
assert.equal(state.embedCalls, 0);
|
|
100
|
+
});
|
|
101
|
+
});
|