Skip to content

Commit 46657ff

Browse files
committed
test: add end-to-end test example for header matching
1 parent 259ebe3 commit 46657ff

File tree

8 files changed

+387
-0
lines changed

8 files changed

+387
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
id: headerMatching
3+
title: Header Matching
4+
description: This example demonstrates how to match raw message headers.
5+
---
6+
import RenderExample from "/src/components/RenderExample.tsx"
7+
import config from '!!raw-loader!../../../../src/examples/advanced/headerMatching.json';
8+
import script from '!!raw-loader!../../../../src/gas/examples/advanced/headerMatching.js';
9+
import { info } from "../../../../src/examples/advanced/headerMatching.ts"
10+
11+
This example demonstrates how to use the `rawHeaders` property in the message matching configuration.
12+
It allows matching messages based on the content of their raw headers using regular expressions.
13+
14+
In this specific example:
15+
1. It first matches threads sent by the current user (`from:${user.email}`).
16+
2. Within those threads, it matches messages where the raw headers contain a `From:` line exactly matching the user's email address (`(?m)^From: ${user.email}$`). Note the use of `(?m)` at the beginning of the regex to allow matching `^` and `$` at start/end of line (multi-line matching).
17+
3. For matching messages, it processes attachments named `invoice.pdf`.
18+
4. Matching attachments are stored in Google Drive using a dynamically generated path based on message and attachment details.
19+
20+
<RenderExample info={info} config={config} script={script} />
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"description": "This example demonstrates how to match raw message headers.",
3+
"settings": {
4+
"markProcessedMethod": "mark-read"
5+
},
6+
"global": {
7+
"thread": {
8+
"match": {
9+
"query": "has:attachment -in:trash -in:drafts -in:spam after:{{date.now|formatDate('yyyy-MM-dd')}} is:unread subject:\"[GmailProcessor-Test] headerMatching\""
10+
}
11+
}
12+
},
13+
"threads": [
14+
{
15+
"match": {
16+
"query": "from:${user.email}"
17+
},
18+
"messages": [
19+
{
20+
"match": {
21+
"rawHeaders": "(?m)^From: ${user.email}$"
22+
},
23+
"attachments": [
24+
{
25+
"match": {
26+
"name": "^invoice\\.pdf$"
27+
},
28+
"actions": [
29+
{
30+
"name": "attachment.store",
31+
"args": {
32+
"location": "/GmailProcessor-Tests/e2e/headerMatching/{{message.date|formatDate('yyyy-MM-dd')}}/{{message.subject}}-{{attachment.name}}",
33+
"conflictStrategy": "keep"
34+
}
35+
}
36+
]
37+
}
38+
]
39+
}
40+
]
41+
}
42+
]
43+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// NOTE: Do not edit this auto-generated file!
2+
// Template: src/templates/test-spec.eta
3+
// Source: src/examples/advanced/headerMatching.ts
4+
5+
import { ProcessingStatus } from "../../lib"
6+
import { GmailProcessor } from "../../lib/processors/GmailProcessor"
7+
import { MockFactory, Mocks } from "../../test/mocks/MockFactory"
8+
import { validateConfig } from "./../../lib/config/Validate"
9+
import { info, runConfig } from "./headerMatching"
10+
11+
let mocks: Mocks
12+
13+
beforeAll(() => {
14+
mocks = MockFactory.newMocks()
15+
})
16+
17+
describe(`Example ${info.name}`, () => {
18+
it(`should successfully run example ${info.name}`, () => {
19+
const result = GmailProcessor.runWithJson(runConfig, [], mocks.envContext)
20+
expect(result.status).toEqual(ProcessingStatus.OK)
21+
expect(result.processedThreadConfigs).toEqual(runConfig.threads?.length)
22+
})
23+
it(`should successfully validate example ${info.name}`, () => {
24+
validateConfig(runConfig)
25+
expect(validateConfig.errors).toBeNull()
26+
})
27+
})
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { ProcessingStatus } from "../../lib/Context"
2+
import { ConflictStrategy } from "../../lib/adapter/GDriveAdapter"
3+
import { Config } from "../../lib/config/Config"
4+
import { MarkProcessedMethod } from "../../lib/config/SettingsConfig"
5+
import { E2EInitConfig, E2ETest, E2ETestConfig } from "../../lib/e2e/E2E"
6+
import { Example, ExampleCategory, ExampleInfo } from "../Example"
7+
import { E2EDefaults } from "./../../lib/e2e/E2EDefaults"
8+
9+
/** This example demonstrates how to use the `rawHeaders` property in the message matching configuration.
10+
* It allows matching messages based on the content of their raw headers using regular expressions.
11+
*
12+
* In this specific example:
13+
* 1. It first matches threads sent by the current user (`from:${user.email}`).
14+
* 2. Within those threads, it matches messages where the raw headers contain a `From:` line exactly matching the user's email address (`(?m)^From: ${user.email}$`). Note the use of `(?m)` at the beginning of the regex to allow matching `^` and `$` at start/end of line (multi-line matching).
15+
* 3. For matching messages, it processes attachments named `invoice.pdf`.
16+
* 4. Matching attachments are stored in Google Drive using a dynamically generated path based on message and attachment details.
17+
*/
18+
export const info: ExampleInfo = {
19+
name: "headerMatching",
20+
title: "Header Matching",
21+
description: "This example demonstrates how to match raw message headers.",
22+
category: ExampleCategory.ADVANCED,
23+
}
24+
25+
export const initConfig: E2EInitConfig = {
26+
mails: [
27+
{
28+
attachments: ["invoice.pdf"],
29+
},
30+
],
31+
}
32+
33+
export const runConfig: Config = {
34+
description: info.description,
35+
settings: {
36+
// ATTENTION: Decide on the method to be used to mark processed threads/messages:
37+
markProcessedMethod: MarkProcessedMethod.MARK_MESSAGE_READ,
38+
// Add more settings if required ...
39+
},
40+
global: {
41+
// Place global thread, message or attachment configuration here
42+
thread: {
43+
match: {
44+
query: `has:attachment -in:trash -in:drafts -in:spam after:{{date.now|formatDate('yyyy-MM-dd')}} is:unread subject:"${E2EDefaults.EMAIL_SUBJECT_PREFIX}${info.name}"`,
45+
},
46+
},
47+
},
48+
threads: [
49+
// Place thread processing config here
50+
{
51+
match: {
52+
query: "from:${user.email}",
53+
},
54+
messages: [
55+
// Place message processing config here
56+
{
57+
match: {
58+
rawHeaders: "(?m)^From: ${user.email}$",
59+
},
60+
attachments: [
61+
// Place attachment processing config here
62+
{
63+
match: {
64+
name: "^invoice\\.pdf$",
65+
},
66+
actions: [
67+
{
68+
name: "attachment.store",
69+
args: {
70+
location: `${E2EDefaults.DRIVE_TESTS_BASE_PATH}/${info.name}/{{message.date|formatDate('yyyy-MM-dd')}}/{{message.subject}}-{{attachment.name}}`,
71+
conflictStrategy: ConflictStrategy.KEEP,
72+
},
73+
},
74+
],
75+
},
76+
],
77+
},
78+
],
79+
},
80+
],
81+
}
82+
83+
export const example: Example = {
84+
info,
85+
config: runConfig,
86+
}
87+
88+
export const tests: E2ETest[] = [
89+
{
90+
message: "No failures",
91+
assertions: [
92+
{
93+
message: "Processing status should be OK",
94+
assertFn: (_testConfig, procResult) =>
95+
procResult.status === ProcessingStatus.OK,
96+
},
97+
{
98+
message: "At least one thread should have been processed",
99+
assertFn: (_testConfig, procResult) => procResult.processedThreads >= 1,
100+
},
101+
{
102+
message: "Expected number of actions should have been executed",
103+
assertFn: (_testConfig, procResult) =>
104+
procResult.executedActions.length ===
105+
procResult.processedThreads + procResult.processedAttachments,
106+
},
107+
],
108+
},
109+
]
110+
111+
export const testConfig: E2ETestConfig = {
112+
example,
113+
info,
114+
initConfig,
115+
runConfig,
116+
tests,
117+
}

src/examples/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { example as actionThreadRemoveLabelExample } from "./actions/actionThrea
66
import { example as customActionsExample } from "./advanced/customActions"
77
import { example as dateExpressionsExample } from "./advanced/dateExpressions"
88
import { example as decryptPdfExample } from "./advanced/decryptPdf"
9+
import { example as headerMatchingExample } from "./advanced/headerMatching"
910
import { example as logSheetLoggingExample } from "./advanced/logSheetLogging"
1011
import { example as regularExpressionsExample } from "./advanced/regularExpressions"
1112
import { example as simpleExample } from "./basics/simple"
@@ -24,6 +25,7 @@ export const allExamples: (Example | V1Example)[] = [
2425
customActionsExample,
2526
dateExpressionsExample,
2627
decryptPdfExample,
28+
headerMatchingExample,
2729
issue301Example,
2830
legacyExpressionsExample,
2931
logSheetLoggingExample,
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// NOTE: Do not edit this auto-generated file!
2+
// Template: src/templates/gas-test.eta
3+
// Source: src/examples/advanced/headerMatching.ts
4+
5+
function headerMatchingTestConfig() {
6+
/** This example demonstrates how to use the `rawHeaders` property in the message matching configuration.
7+
* It allows matching messages based on the content of their raw headers using regular expressions.
8+
*
9+
* In this specific example:
10+
* 1. It first matches threads sent by the current user (`from:${user.email}`).
11+
* 2. Within those threads, it matches messages where the raw headers contain a `From:` line exactly matching the user's email address (`(?m)^From: ${user.email}$`). Note the use of `(?m)` at the beginning of the regex to allow matching `^` and `$` at start/end of line (multi-line matching).
12+
* 3. For matching messages, it processes attachments named `invoice.pdf`.
13+
* 4. Matching attachments are stored in Google Drive using a dynamically generated path based on message and attachment details.
14+
*/
15+
const info = {
16+
name: "headerMatching",
17+
title: "Header Matching",
18+
description: "This example demonstrates how to match raw message headers.",
19+
category: "advanced",
20+
}
21+
22+
const initConfig = {
23+
mails: [
24+
{
25+
attachments: ["invoice.pdf"],
26+
},
27+
],
28+
}
29+
30+
const runConfig = {
31+
description: info.description,
32+
settings: {
33+
// ATTENTION: Decide on the method to be used to mark processed threads/messages:
34+
markProcessedMethod:
35+
GmailProcessorLib.MarkProcessedMethod.MARK_MESSAGE_READ,
36+
// Add more settings if required ...
37+
},
38+
global: {
39+
// Place global thread, message or attachment configuration here
40+
thread: {
41+
match: {
42+
query: `has:attachment -in:trash -in:drafts -in:spam after:{{date.now|formatDate('yyyy-MM-dd')}} is:unread subject:"${GmailProcessorLib.E2EDefaults.EMAIL_SUBJECT_PREFIX}${info.name}"`,
43+
},
44+
},
45+
},
46+
threads: [
47+
// Place thread processing config here
48+
{
49+
match: {
50+
query: "from:${user.email}",
51+
},
52+
messages: [
53+
// Place message processing config here
54+
{
55+
match: {
56+
rawHeaders: "(?m)^From: ${user.email}$",
57+
},
58+
attachments: [
59+
// Place attachment processing config here
60+
{
61+
match: {
62+
name: "^invoice\\.pdf$",
63+
},
64+
actions: [
65+
{
66+
name: "attachment.store",
67+
args: {
68+
location: `${GmailProcessorLib.E2EDefaults.DRIVE_TESTS_BASE_PATH}/${info.name}/{{message.date|formatDate('yyyy-MM-dd')}}/{{message.subject}}-{{attachment.name}}`,
69+
conflictStrategy: GmailProcessorLib.ConflictStrategy.KEEP,
70+
},
71+
},
72+
],
73+
},
74+
],
75+
},
76+
],
77+
},
78+
],
79+
}
80+
81+
const example = {
82+
info,
83+
config: runConfig,
84+
}
85+
86+
const tests = [
87+
{
88+
message: "No failures",
89+
assertions: [
90+
{
91+
message: "Processing status should be OK",
92+
assertFn: (_testConfig, procResult) =>
93+
procResult.status === GmailProcessorLib.ProcessingStatus.OK,
94+
},
95+
{
96+
message: "At least one thread should have been processed",
97+
assertFn: (_testConfig, procResult) =>
98+
procResult.processedThreads >= 1,
99+
},
100+
{
101+
message: "Expected number of actions should have been executed",
102+
assertFn: (_testConfig, procResult) =>
103+
procResult.executedActions.length ===
104+
procResult.processedThreads + procResult.processedAttachments,
105+
},
106+
],
107+
},
108+
]
109+
110+
const testConfig = {
111+
example,
112+
info,
113+
initConfig,
114+
runConfig,
115+
tests,
116+
}
117+
return testConfig
118+
}
119+
120+
function headerMatchingTest() {
121+
const testConfig = headerMatchingTestConfig()
122+
return GmailProcessorLib.E2E.runTests(
123+
testConfig,
124+
false,
125+
E2E_REPO_BRANCH,
126+
GmailProcessorLib.RunMode.DANGEROUS,
127+
)
128+
}

0 commit comments

Comments
 (0)