Skip to content

Commit 1b02e06

Browse files
test(e2e): add bulk email sending e2e tests
- Fix sendEmail e2e test type guards for ProcessBulkRequestResponse | SendEmailSingleResponse - Add sendEmailBulk e2e test file for bulk processing - Update error message expectations for processProtectedData errors - Test bulk email sending with grantedAccess array
1 parent d0fb138 commit 1b02e06

File tree

2 files changed

+240
-9
lines changed

2 files changed

+240
-9
lines changed

tests/e2e/sendEmail.test.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ describe('web3mail.sendEmail()', () => {
145145
emailSubject: 'e2e mail object for test',
146146
emailContent: 'e2e mail content for test',
147147
protectedData: validProtectedData.address,
148+
workerpoolAddressOrEns: TEST_CHAIN.prodWorkerpool,
148149
workerpoolMaxPrice: prodWorkerpoolPublicPrice,
149150
})
150151
.catch((e) => (error = e));
@@ -171,7 +172,10 @@ describe('web3mail.sendEmail()', () => {
171172
protectedData: validProtectedData.address,
172173
workerpoolMaxPrice: prodWorkerpoolPublicPrice,
173174
});
174-
expect(sendEmailResponse.taskId).toBeDefined();
175+
expect('taskId' in sendEmailResponse).toBe(true);
176+
expect(
177+
(sendEmailResponse as { taskId: string }).taskId
178+
).toBeDefined();
175179
},
176180
2 * MAX_EXPECTED_BLOCKTIME + MAX_EXPECTED_WEB2_SERVICES_TIME
177181
);
@@ -273,7 +277,8 @@ describe('web3mail.sendEmail()', () => {
273277
protectedData: validProtectedData.address,
274278
workerpoolAddressOrEns: learnProdWorkerpoolAddress,
275279
});
276-
expect(sendEmailResponse.taskId).toBeDefined();
280+
expect('taskId' in sendEmailResponse).toBe(true);
281+
expect((sendEmailResponse as { taskId: string }).taskId).toBeDefined();
277282
},
278283
2 * MAX_EXPECTED_BLOCKTIME + MAX_EXPECTED_WEB2_SERVICES_TIME
279284
);
@@ -303,7 +308,8 @@ describe('web3mail.sendEmail()', () => {
303308
protectedData: protectedDataForWhitelist.address,
304309
workerpoolAddressOrEns: learnProdWorkerpoolAddress,
305310
});
306-
expect(sendEmailResponse.taskId).toBeDefined();
311+
expect('taskId' in sendEmailResponse).toBe(true);
312+
expect((sendEmailResponse as { taskId: string }).taskId).toBeDefined();
307313
},
308314
2 * MAX_EXPECTED_BLOCKTIME + MAX_EXPECTED_WEB2_SERVICES_TIME
309315
);
@@ -319,7 +325,8 @@ describe('web3mail.sendEmail()', () => {
319325
contentType: 'text/html',
320326
workerpoolAddressOrEns: learnProdWorkerpoolAddress,
321327
});
322-
expect(sendEmailResponse.taskId).toBeDefined();
328+
expect('taskId' in sendEmailResponse).toBe(true);
329+
expect((sendEmailResponse as { taskId: string }).taskId).toBeDefined();
323330
},
324331
2 * MAX_EXPECTED_BLOCKTIME + MAX_EXPECTED_WEB2_SERVICES_TIME
325332
);
@@ -334,7 +341,8 @@ describe('web3mail.sendEmail()', () => {
334341
senderName: 'Product Team',
335342
workerpoolAddressOrEns: learnProdWorkerpoolAddress,
336343
});
337-
expect(sendEmailResponse.taskId).toBeDefined();
344+
expect('taskId' in sendEmailResponse).toBe(true);
345+
expect((sendEmailResponse as { taskId: string }).taskId).toBeDefined();
338346
},
339347
2 * MAX_EXPECTED_BLOCKTIME + MAX_EXPECTED_WEB2_SERVICES_TIME
340348
);
@@ -353,7 +361,8 @@ describe('web3mail.sendEmail()', () => {
353361
senderName: 'Product Team',
354362
workerpoolAddressOrEns: learnProdWorkerpoolAddress,
355363
});
356-
expect(sendEmailResponse.taskId).toBeDefined();
364+
expect('taskId' in sendEmailResponse).toBe(true);
365+
expect((sendEmailResponse as { taskId: string }).taskId).toBeDefined();
357366
},
358367
2 * MAX_EXPECTED_BLOCKTIME + MAX_EXPECTED_WEB2_SERVICES_TIME
359368
);
@@ -368,7 +377,8 @@ describe('web3mail.sendEmail()', () => {
368377
workerpoolAddressOrEns: learnProdWorkerpoolAddress,
369378
label: 'ID1234678',
370379
});
371-
expect(sendEmailResponse.taskId).toBeDefined();
380+
expect('taskId' in sendEmailResponse).toBe(true);
381+
expect((sendEmailResponse as { taskId: string }).taskId).toBeDefined();
372382
// TODO check label in created deal
373383
},
374384
2 * MAX_EXPECTED_BLOCKTIME + MAX_EXPECTED_WEB2_SERVICES_TIME
@@ -457,7 +467,10 @@ describe('web3mail.sendEmail()', () => {
457467
// workerpoolAddressOrEns: prodWorkerpoolAddress, // default
458468
useVoucher: true,
459469
});
460-
expect(sendEmailResponse.taskId).toBeDefined();
470+
expect('taskId' in sendEmailResponse).toBe(true);
471+
expect(
472+
(sendEmailResponse as { taskId: string }).taskId
473+
).toBeDefined();
461474
},
462475
2 * MAX_EXPECTED_BLOCKTIME +
463476
MAX_EXPECTED_WEB2_SERVICES_TIME +
@@ -567,7 +580,10 @@ describe('web3mail.sendEmail()', () => {
567580
workerpoolMaxPrice: nonSponsoredAmount,
568581
useVoucher: true,
569582
});
570-
expect(sendEmailResponse.taskId).toBeDefined();
583+
expect('taskId' in sendEmailResponse).toBe(true);
584+
expect(
585+
(sendEmailResponse as { taskId: string }).taskId
586+
).toBeDefined();
571587
},
572588
2 * MAX_EXPECTED_BLOCKTIME +
573589
MAX_EXPECTED_WEB2_SERVICES_TIME +

tests/e2e/sendEmailBulk.test.ts

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
import {
2+
IExecDataProtectorCore,
3+
ProcessBulkRequestResponse,
4+
ProtectedDataWithSecretProps,
5+
} from '@iexec/dataprotector';
6+
import { beforeAll, beforeEach, describe, expect, it } from '@jest/globals';
7+
import { HDNodeWallet } from 'ethers';
8+
import {
9+
DEFAULT_CHAIN_ID,
10+
getChainDefaultConfig,
11+
} from '../../src/config/config.js';
12+
import {
13+
Contact,
14+
IExecWeb3mail,
15+
SendEmailSingleResponse,
16+
} from '../../src/index.js';
17+
import {
18+
MAX_EXPECTED_BLOCKTIME,
19+
MAX_EXPECTED_WEB2_SERVICES_TIME,
20+
MAX_EXPECTED_SUBGRAPH_INDEXING_TIME,
21+
TEST_CHAIN,
22+
createAndPublishAppOrders,
23+
createAndPublishWorkerpoolOrder,
24+
ensureSufficientStake,
25+
getRandomWallet,
26+
getTestConfig,
27+
getTestIExecOption,
28+
getTestWeb3SignerProvider,
29+
waitSubgraphIndexing,
30+
} from '../test-utils.js';
31+
import { IExec } from 'iexec';
32+
import { NULL_ADDRESS } from 'iexec/utils';
33+
34+
describe('web3mail.sendEmail() - Bulk Processing', () => {
35+
let consumerWallet: HDNodeWallet;
36+
let providerWallet: HDNodeWallet;
37+
let web3mail: IExecWeb3mail;
38+
let dataProtector: IExecDataProtectorCore;
39+
let validProtectedData1: ProtectedDataWithSecretProps;
40+
let validProtectedData2: ProtectedDataWithSecretProps;
41+
let validProtectedData3: ProtectedDataWithSecretProps;
42+
let consumerIExecInstance: IExec;
43+
let consumerDataProtectorInstance: IExecDataProtectorCore;
44+
const iexecOptions = getTestIExecOption();
45+
const prodWorkerpoolPublicPrice = 1000;
46+
const defaultConfig = getChainDefaultConfig(DEFAULT_CHAIN_ID);
47+
48+
beforeAll(async () => {
49+
// Create workerpool orders
50+
await createAndPublishWorkerpoolOrder(
51+
TEST_CHAIN.prodWorkerpool,
52+
TEST_CHAIN.prodWorkerpoolOwnerWallet,
53+
NULL_ADDRESS,
54+
1_000,
55+
prodWorkerpoolPublicPrice
56+
);
57+
58+
// Create app orders
59+
providerWallet = getRandomWallet();
60+
const ethProvider = getTestWeb3SignerProvider(
61+
TEST_CHAIN.appOwnerWallet.privateKey
62+
);
63+
const resourceProvider = new IExec({ ethProvider }, iexecOptions);
64+
await createAndPublishAppOrders(
65+
resourceProvider,
66+
defaultConfig!.dappAddress
67+
);
68+
69+
dataProtector = new IExecDataProtectorCore(
70+
...getTestConfig(providerWallet.privateKey)
71+
);
72+
73+
// create valid protected data
74+
validProtectedData1 = await dataProtector.protectData({
75+
data: { email: '[email protected]' },
76+
name: 'bulk test 1',
77+
});
78+
79+
validProtectedData2 = await dataProtector.protectData({
80+
data: { email: '[email protected]' },
81+
name: 'bulk test 2',
82+
});
83+
84+
validProtectedData3 = await dataProtector.protectData({
85+
data: { email: '[email protected]' },
86+
name: 'bulk test 3',
87+
});
88+
89+
await waitSubgraphIndexing();
90+
}, 5 * MAX_EXPECTED_BLOCKTIME + MAX_EXPECTED_WEB2_SERVICES_TIME + 5_000);
91+
92+
beforeEach(async () => {
93+
consumerWallet = getRandomWallet();
94+
const consumerEthProvider = getTestWeb3SignerProvider(
95+
consumerWallet.privateKey
96+
);
97+
consumerIExecInstance = new IExec(
98+
{ ethProvider: consumerEthProvider },
99+
iexecOptions
100+
);
101+
102+
// Grant access with allowBulk for bulk processing
103+
await dataProtector.grantAccess({
104+
authorizedApp: defaultConfig.dappAddress,
105+
protectedData: validProtectedData1.address,
106+
authorizedUser: consumerWallet.address,
107+
allowBulk: true,
108+
});
109+
110+
await dataProtector.grantAccess({
111+
authorizedApp: defaultConfig.dappAddress,
112+
protectedData: validProtectedData2.address,
113+
authorizedUser: consumerWallet.address,
114+
allowBulk: true,
115+
});
116+
117+
await dataProtector.grantAccess({
118+
authorizedApp: defaultConfig.dappAddress,
119+
protectedData: validProtectedData3.address,
120+
authorizedUser: consumerWallet.address,
121+
allowBulk: true,
122+
});
123+
124+
await waitSubgraphIndexing();
125+
126+
web3mail = new IExecWeb3mail(...getTestConfig(consumerWallet.privateKey));
127+
consumerDataProtectorInstance = new IExecDataProtectorCore(
128+
...getTestConfig(consumerWallet.privateKey)
129+
);
130+
}, MAX_EXPECTED_BLOCKTIME + MAX_EXPECTED_SUBGRAPH_INDEXING_TIME);
131+
132+
describe('Bulk email sending', () => {
133+
it(
134+
'should successfully process bulk request',
135+
async () => {
136+
// Fetch contacts with allowBulk access
137+
const contacts: Contact[] = await web3mail.fetchMyContacts();
138+
expect(contacts.length).toBeGreaterThanOrEqual(3);
139+
140+
// Ensure consumer has sufficient stake
141+
await ensureSufficientStake(
142+
consumerIExecInstance,
143+
prodWorkerpoolPublicPrice
144+
);
145+
146+
const bulkOrders = contacts.map((contact) => contact.grantedAccess);
147+
148+
// Prepare email content encryption and upload (like sendEmail does for single)
149+
const emailSubject = 'Bulk test subject';
150+
const emailContent = 'Bulk test message';
151+
const iexec = consumerIExecInstance;
152+
const emailContentEncryptionKey = iexec.dataset.generateEncryptionKey();
153+
const encryptedFile = await iexec.dataset.encrypt(
154+
Buffer.from(emailContent, 'utf8'),
155+
emailContentEncryptionKey
156+
);
157+
158+
// Upload to IPFS using local test configuration
159+
const { add } = await import('../../src/utils/ipfs-service.js');
160+
const cid = await add(encryptedFile, {
161+
ipfsNode: TEST_CHAIN.ipfsNode,
162+
ipfsGateway: TEST_CHAIN.ipfsGateway,
163+
});
164+
const multiaddr = `/ipfs/${cid}`;
165+
166+
// Prepare secrets like sendEmail does
167+
const secrets = {
168+
1: JSON.stringify({
169+
senderName: 'Bulk Test Sender',
170+
emailSubject: emailSubject,
171+
emailContentMultiAddr: multiaddr,
172+
contentType: 'text/plain',
173+
emailContentEncryptionKey,
174+
useCallback: true,
175+
}),
176+
};
177+
178+
// Prepare the bulk request using the contacts
179+
await consumerDataProtectorInstance.prepareBulkRequest({
180+
bulkOrders,
181+
app: defaultConfig.dappAddress,
182+
workerpool: TEST_CHAIN.prodWorkerpool,
183+
secrets,
184+
maxProtectedDataPerTask: 3,
185+
appMaxPrice: 1000,
186+
workerpoolMaxPrice: 1000,
187+
});
188+
189+
// Process the bulk request
190+
const result: ProcessBulkRequestResponse | SendEmailSingleResponse =
191+
await web3mail.sendEmail({
192+
emailSubject: 'Bulk test subject',
193+
emailContent: 'Bulk test message',
194+
// protectedData is optional when grantedAccess is provided
195+
grantedAccess: bulkOrders,
196+
maxProtectedDataPerTask: 3,
197+
workerpoolMaxPrice: prodWorkerpoolPublicPrice,
198+
});
199+
200+
// Verify the result
201+
expect(result).toBeDefined();
202+
expect('tasks' in result).toBe(true);
203+
const tasks = 'tasks' in result ? result.tasks : [];
204+
expect(Array.isArray(tasks)).toBe(true);
205+
expect(tasks.length).toBeGreaterThan(0);
206+
tasks.forEach((task) => {
207+
expect(task.taskId).toBeDefined();
208+
expect(task.dealId).toBeDefined();
209+
expect(task.bulkIndex).toBeDefined();
210+
});
211+
},
212+
30 * MAX_EXPECTED_BLOCKTIME + MAX_EXPECTED_WEB2_SERVICES_TIME + 60_000
213+
);
214+
});
215+
});

0 commit comments

Comments
 (0)