-
Notifications
You must be signed in to change notification settings - Fork 590
/
Copy pathai-choose-rule.test.ts
372 lines (331 loc) · 9.51 KB
/
ai-choose-rule.test.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
import { describe, expect, test, vi } from "vitest";
import { aiChooseRule } from "@/utils/ai/choose-rule/ai-choose-rule";
import { type Action, ActionType, LogicalOperator } from "@prisma/client";
import { defaultReplyTrackerInstructions } from "@/utils/reply-tracker/consts";
// pnpm test-ai ai-choose-rule
const isAiTest = process.env.RUN_AI_TESTS === "true";
vi.mock("server-only", () => ({}));
describe.runIf(isAiTest)("aiChooseRule", () => {
test("Should return no rule when no rules passed", async () => {
const result = await aiChooseRule({
rules: [],
email: getEmail(),
user: getUser(),
});
expect(result).toEqual({ reason: "No rules" });
});
test("Should return correct rule when only one rule passed", async () => {
const rule = getRule(
"Match emails that have the word 'test' in the subject line",
);
const result = await aiChooseRule({
email: getEmail({ subject: "test" }),
rules: [rule],
user: getUser(),
});
expect(result).toEqual({
rule,
reason: expect.any(String),
});
});
test("Should return correct rule when multiple rules passed", async () => {
const rule1 = getRule(
"Match emails that have the word 'test' in the subject line",
);
const rule2 = getRule(
"Match emails that have the word 'remember' in the subject line",
);
const result = await aiChooseRule({
rules: [rule1, rule2],
email: getEmail({ subject: "remember that call" }),
user: getUser(),
});
expect(result).toEqual({
rule: rule2,
reason: expect.any(String),
});
});
test("Should generate action arguments", async () => {
const rule1 = getRule(
"Match emails that have the word 'question' in the subject line",
);
const rule2 = getRule("Match emails asking for a joke", [
{
id: "id",
createdAt: new Date(),
updatedAt: new Date(),
type: ActionType.REPLY,
ruleId: "ruleId",
label: null,
subject: null,
content: "{{Write a joke}}",
to: null,
cc: null,
bcc: null,
url: null,
},
]);
const result = await aiChooseRule({
rules: [rule1, rule2],
email: getEmail({
subject: "Joke",
content: "Tell me a joke about sheep",
}),
user: getUser(),
});
expect(result).toEqual({
rule: rule2,
reason: expect.any(String),
});
});
describe("Complex real-world rule scenarios", () => {
const recruiters = getRule(
"Match emails from recruiters or about job opportunities",
);
const legal = getRule(
"Match emails containing legal documents or contracts",
);
const requiresResponse = getRule(defaultReplyTrackerInstructions);
const productUpdates = getRule(
"Match emails about product updates or feature announcements",
);
const financial = getRule(
"Match emails containing financial information or invoices",
);
const technicalIssues = getRule(
"Match emails about technical issues like server downtime or bug reports",
);
const marketing = getRule(
"Match emails containing marketing or promotional content",
);
const teamUpdates = getRule(
"Match emails about team updates or internal communications",
);
const customerFeedback = getRule(
"Match emails about customer feedback or support requests",
);
const events = getRule(
"Match emails containing event invitations or RSVPs",
);
const projectDeadlines = getRule(
"Match emails about project deadlines or milestones",
);
const urgent = getRule("Match urgent emails requiring immediate attention");
const catchAll = getRule("Match emails that don't fit any other category");
const rules = [
recruiters,
legal,
requiresResponse,
productUpdates,
financial,
technicalIssues,
marketing,
teamUpdates,
customerFeedback,
events,
projectDeadlines,
urgent,
catchAll,
];
test("Should match simple response required", async () => {
const result = await aiChooseRule({
rules,
email: getEmail({
from: "[email protected]",
subject: "Can we meet for lunch tomorrow?",
content: "LMK\n\n--\nAlice Smith,\nCEO, The Boring Fund",
}),
user: getUser(),
});
expect(result).toEqual({
rule: requiresResponse,
reason: expect.any(String),
});
});
test("Should match technical issues", async () => {
const result = await aiChooseRule({
rules,
email: getEmail({
subject: "Server downtime reported",
content:
"We're experiencing critical server issues affecting production.",
}),
user: getUser(),
});
expect(result).toEqual({
rule: technicalIssues,
reason: expect.any(String),
});
});
test("Should match financial emails", async () => {
const result = await aiChooseRule({
rules,
email: getEmail({
subject: "Your invoice for March 2024",
content: "Please find attached your invoice for services rendered.",
}),
user: getUser(),
});
expect(result).toEqual({
rule: financial,
reason: expect.any(String),
});
});
test("Should match recruiter emails", async () => {
const result = await aiChooseRule({
rules,
email: getEmail({
subject: "New job opportunity at Tech Corp",
content:
"I came across your profile and think you'd be perfect for...",
}),
user: getUser(),
});
expect(result).toEqual({
rule: recruiters,
reason: expect.any(String),
});
});
test("Should match legal documents", async () => {
const result = await aiChooseRule({
rules,
email: getEmail({
subject: "Please review: Contract for new project",
content: "Attached is the contract for your review and signature.",
}),
user: getUser(),
});
expect(result).toEqual({
rule: legal,
reason: expect.any(String),
});
});
test("Should match emails requiring response", async () => {
const result = await aiChooseRule({
rules,
email: getEmail({
subject: "Team lunch tomorrow?",
content: "Would you like to join us for team lunch tomorrow at 12pm?",
}),
user: getUser(),
});
expect(result).toEqual({
rule: requiresResponse,
reason: expect.any(String),
});
});
test("Should match product updates", async () => {
const result = await aiChooseRule({
rules,
email: getEmail({
subject: "New Feature Release: AI Integration",
content: "We're excited to announce our new AI features...",
}),
user: getUser(),
});
expect(result).toEqual({
rule: productUpdates,
reason: expect.any(String),
});
});
test("Should match marketing emails", async () => {
const result = await aiChooseRule({
rules,
email: getEmail({
subject: "50% off Spring Sale!",
content: "Don't miss out on our biggest sale of the season!",
}),
user: getUser(),
});
expect(result).toEqual({
rule: marketing,
reason: expect.any(String),
});
});
test("Should match team updates", async () => {
const result = await aiChooseRule({
rules,
email: getEmail({
subject: "Weekly Team Update",
content: "Here's what the team accomplished this week...",
}),
user: getUser(),
});
expect(result).toEqual({
rule: teamUpdates,
reason: expect.any(String),
});
});
test("Should match customer feedback", async () => {
const result = await aiChooseRule({
rules,
email: getEmail({
subject: "Customer Feedback: App Performance",
content: "I've been experiencing slow loading times...",
}),
user: getUser(),
});
expect(result).toEqual({
rule: customerFeedback,
reason: expect.any(String),
});
});
test("Should match event invitations", async () => {
const result = await aiChooseRule({
rules,
email: getEmail({
subject: "Invitation: Annual Tech Conference",
content: "You're invited to speak at our annual conference...",
}),
user: getUser(),
});
expect(result).toEqual({
rule: events,
reason: expect.any(String),
});
});
});
});
// helpers
function getRule(instructions: string, actions: Action[] = []) {
return {
instructions,
name: "Joke requests",
actions,
id: "id",
userId: "userId",
createdAt: new Date(),
updatedAt: new Date(),
automate: false,
runOnThreads: false,
groupId: null,
from: null,
subject: null,
body: null,
to: null,
enabled: true,
categoryFilterType: null,
conditionalOperator: LogicalOperator.AND,
};
}
function getEmail({
from = "[email protected]",
subject = "subject",
content = "content",
}: { from?: string; subject?: string; content?: string } = {}) {
return {
id: "id",
from,
subject,
content,
};
}
function getUser() {
return {
aiModel: null,
aiProvider: null,
email: "[email protected]",
aiApiKey: null,
about: null,
};
}