@bernierllc/email-sender-manager 1.0.1
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/LICENSE +5 -0
- package/README.md +466 -0
- package/dist/__tests__/manager.test.d.ts +2 -0
- package/dist/__tests__/manager.test.d.ts.map +1 -0
- package/dist/__tests__/manager.test.js +344 -0
- package/dist/__tests__/manager.test.js.map +1 -0
- package/dist/__tests__/mocks/mock-database.d.ts +15 -0
- package/dist/__tests__/mocks/mock-database.d.ts.map +1 -0
- package/dist/__tests__/mocks/mock-database.js +219 -0
- package/dist/__tests__/mocks/mock-database.js.map +1 -0
- package/dist/__tests__/selection/selector.test.d.ts +2 -0
- package/dist/__tests__/selection/selector.test.d.ts.map +1 -0
- package/dist/__tests__/selection/selector.test.js +351 -0
- package/dist/__tests__/selection/selector.test.js.map +1 -0
- package/dist/__tests__/utils/domain-utils.test.d.ts +2 -0
- package/dist/__tests__/utils/domain-utils.test.d.ts.map +1 -0
- package/dist/__tests__/utils/domain-utils.test.js +91 -0
- package/dist/__tests__/utils/domain-utils.test.js.map +1 -0
- package/dist/__tests__/utils/validation-utils.test.d.ts +2 -0
- package/dist/__tests__/utils/validation-utils.test.d.ts.map +1 -0
- package/dist/__tests__/utils/validation-utils.test.js +117 -0
- package/dist/__tests__/utils/validation-utils.test.js.map +1 -0
- package/dist/database/bootstrap.d.ts +35 -0
- package/dist/database/bootstrap.d.ts.map +1 -0
- package/dist/database/bootstrap.js +183 -0
- package/dist/database/bootstrap.js.map +1 -0
- package/dist/database/schema.d.ts +13 -0
- package/dist/database/schema.d.ts.map +1 -0
- package/dist/database/schema.js +72 -0
- package/dist/database/schema.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/manager.d.ts +85 -0
- package/dist/manager.d.ts.map +1 -0
- package/dist/manager.js +469 -0
- package/dist/manager.js.map +1 -0
- package/dist/selection/selector.d.ts +27 -0
- package/dist/selection/selector.d.ts.map +1 -0
- package/dist/selection/selector.js +107 -0
- package/dist/selection/selector.js.map +1 -0
- package/dist/types.d.ts +257 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/domain-utils.d.ts +21 -0
- package/dist/utils/domain-utils.d.ts.map +1 -0
- package/dist/utils/domain-utils.js +65 -0
- package/dist/utils/domain-utils.js.map +1 -0
- package/dist/utils/validation-utils.d.ts +23 -0
- package/dist/utils/validation-utils.d.ts.map +1 -0
- package/dist/utils/validation-utils.js +148 -0
- package/dist/utils/validation-utils.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2025 Bernier LLC
|
|
3
|
+
|
|
4
|
+
This file is licensed to the client under a limited-use license.
|
|
5
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
6
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
7
|
+
*/
|
|
8
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
9
|
+
/* global describe, it, expect, beforeEach */
|
|
10
|
+
import { SenderSelector } from '../../selection/selector.js';
|
|
11
|
+
describe('SenderSelector', () => {
|
|
12
|
+
let selector;
|
|
13
|
+
let testSenders;
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
const config = {
|
|
16
|
+
strategy: 'domain_match',
|
|
17
|
+
fallbackToDefault: true,
|
|
18
|
+
domainMatchingRules: [],
|
|
19
|
+
};
|
|
20
|
+
selector = new SenderSelector(config);
|
|
21
|
+
// Create test senders
|
|
22
|
+
testSenders = [
|
|
23
|
+
{
|
|
24
|
+
id: 'sender-1',
|
|
25
|
+
name: 'Example Sender',
|
|
26
|
+
fromEmail: 'noreply@example.com',
|
|
27
|
+
fromName: 'Example',
|
|
28
|
+
provider: 'sendgrid',
|
|
29
|
+
isVerified: true,
|
|
30
|
+
verificationStatus: 'verified',
|
|
31
|
+
isDefault: true,
|
|
32
|
+
isActive: true,
|
|
33
|
+
priority: 1,
|
|
34
|
+
domain: 'example.com',
|
|
35
|
+
createdAt: new Date('2025-01-01'),
|
|
36
|
+
updatedAt: new Date('2025-01-01'),
|
|
37
|
+
createdBy: 'admin',
|
|
38
|
+
lastModifiedBy: 'admin',
|
|
39
|
+
lastVerifiedAt: new Date('2025-01-10'),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: 'sender-2',
|
|
43
|
+
name: 'App Sender',
|
|
44
|
+
fromEmail: 'noreply@app.example.com',
|
|
45
|
+
fromName: 'App',
|
|
46
|
+
provider: 'sendgrid',
|
|
47
|
+
isVerified: true,
|
|
48
|
+
verificationStatus: 'verified',
|
|
49
|
+
isDefault: false,
|
|
50
|
+
isActive: true,
|
|
51
|
+
priority: 10,
|
|
52
|
+
domain: 'app.example.com',
|
|
53
|
+
createdAt: new Date('2025-01-02'),
|
|
54
|
+
updatedAt: new Date('2025-01-02'),
|
|
55
|
+
createdBy: 'admin',
|
|
56
|
+
lastModifiedBy: 'admin',
|
|
57
|
+
lastVerifiedAt: new Date('2025-01-09'),
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'sender-3',
|
|
61
|
+
name: 'Marketing Sender',
|
|
62
|
+
fromEmail: 'marketing@example.com',
|
|
63
|
+
fromName: 'Marketing',
|
|
64
|
+
provider: 'mailgun',
|
|
65
|
+
isVerified: false,
|
|
66
|
+
verificationStatus: 'pending',
|
|
67
|
+
isDefault: false,
|
|
68
|
+
isActive: true,
|
|
69
|
+
priority: 5,
|
|
70
|
+
domain: 'example.com',
|
|
71
|
+
createdAt: new Date('2025-01-03'),
|
|
72
|
+
updatedAt: new Date('2025-01-03'),
|
|
73
|
+
createdBy: 'admin',
|
|
74
|
+
lastModifiedBy: 'admin',
|
|
75
|
+
allowedDomains: ['*.example.com', 'other.com'],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'sender-4',
|
|
79
|
+
name: 'Support Sender',
|
|
80
|
+
fromEmail: 'support@example.com',
|
|
81
|
+
fromName: 'Support',
|
|
82
|
+
provider: 'sendgrid',
|
|
83
|
+
isVerified: true,
|
|
84
|
+
verificationStatus: 'verified',
|
|
85
|
+
isDefault: false,
|
|
86
|
+
isActive: true,
|
|
87
|
+
priority: 3,
|
|
88
|
+
domain: 'example.com',
|
|
89
|
+
createdAt: new Date('2025-01-04'),
|
|
90
|
+
updatedAt: new Date('2025-01-04'),
|
|
91
|
+
createdBy: 'admin',
|
|
92
|
+
lastModifiedBy: 'admin',
|
|
93
|
+
lastVerifiedAt: new Date('2025-01-08'),
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
});
|
|
97
|
+
describe('selectBestSender', () => {
|
|
98
|
+
it('should return null for empty candidates', () => {
|
|
99
|
+
const result = selector.selectBestSender([], 'example.com');
|
|
100
|
+
expect(result).toBeNull();
|
|
101
|
+
});
|
|
102
|
+
it('should select by domain match strategy', () => {
|
|
103
|
+
const result = selector.selectBestSender(testSenders, 'example.com', {
|
|
104
|
+
strategy: 'domain_match',
|
|
105
|
+
});
|
|
106
|
+
expect(result).toBeDefined();
|
|
107
|
+
expect(result?.domain).toBe('example.com');
|
|
108
|
+
expect(result?.priority).toBe(1); // Highest priority for exact match
|
|
109
|
+
});
|
|
110
|
+
it('should select by priority strategy', () => {
|
|
111
|
+
const result = selector.selectBestSender(testSenders, 'example.com', {
|
|
112
|
+
strategy: 'priority',
|
|
113
|
+
});
|
|
114
|
+
expect(result).toBeDefined();
|
|
115
|
+
expect(result?.priority).toBe(1);
|
|
116
|
+
expect(result?.id).toBe('sender-1');
|
|
117
|
+
});
|
|
118
|
+
it('should select by round robin strategy', () => {
|
|
119
|
+
const candidates = [testSenders[0], testSenders[1]];
|
|
120
|
+
const result1 = selector.selectBestSender(candidates, 'example.com', {
|
|
121
|
+
strategy: 'round_robin',
|
|
122
|
+
});
|
|
123
|
+
expect(result1?.id).toBe('sender-1');
|
|
124
|
+
const result2 = selector.selectBestSender(candidates, 'example.com', {
|
|
125
|
+
strategy: 'round_robin',
|
|
126
|
+
});
|
|
127
|
+
expect(result2?.id).toBe('sender-2');
|
|
128
|
+
const result3 = selector.selectBestSender(candidates, 'example.com', {
|
|
129
|
+
strategy: 'round_robin',
|
|
130
|
+
});
|
|
131
|
+
expect(result3?.id).toBe('sender-1'); // Wraps around
|
|
132
|
+
});
|
|
133
|
+
it('should use default strategy from config', () => {
|
|
134
|
+
const result = selector.selectBestSender(testSenders, 'example.com');
|
|
135
|
+
expect(result).toBeDefined();
|
|
136
|
+
// Should use domain_match strategy
|
|
137
|
+
expect(result?.domain).toBe('example.com');
|
|
138
|
+
});
|
|
139
|
+
it('should handle unknown strategy gracefully', () => {
|
|
140
|
+
const result = selector.selectBestSender(testSenders, 'example.com', {
|
|
141
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
142
|
+
strategy: 'unknown',
|
|
143
|
+
});
|
|
144
|
+
expect(result).toBeDefined();
|
|
145
|
+
expect(result?.id).toBe('sender-1'); // Returns first candidate
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
describe('selectByDomainMatch', () => {
|
|
149
|
+
it('should find exact domain match', () => {
|
|
150
|
+
const result = selector.selectBestSender(testSenders, 'example.com', {
|
|
151
|
+
strategy: 'domain_match',
|
|
152
|
+
});
|
|
153
|
+
expect(result).toBeDefined();
|
|
154
|
+
expect(result?.domain).toBe('example.com');
|
|
155
|
+
expect(result?.priority).toBe(1); // Highest priority among exact matches
|
|
156
|
+
});
|
|
157
|
+
it('should find subdomain match', () => {
|
|
158
|
+
const result = selector.selectBestSender(testSenders, 'api.example.com', {
|
|
159
|
+
strategy: 'domain_match',
|
|
160
|
+
});
|
|
161
|
+
expect(result).toBeDefined();
|
|
162
|
+
// Should match parent domain example.com
|
|
163
|
+
expect(result?.domain).toBe('example.com');
|
|
164
|
+
});
|
|
165
|
+
it('should find parent domain match', () => {
|
|
166
|
+
const result = selector.selectBestSender(testSenders, 'app.example.com', {
|
|
167
|
+
strategy: 'domain_match',
|
|
168
|
+
});
|
|
169
|
+
expect(result).toBeDefined();
|
|
170
|
+
expect(result?.domain).toBe('app.example.com'); // Exact match first
|
|
171
|
+
});
|
|
172
|
+
it('should match allowed domains with wildcards', () => {
|
|
173
|
+
const result = selector.selectBestSender(testSenders, 'other.com', {
|
|
174
|
+
strategy: 'domain_match',
|
|
175
|
+
});
|
|
176
|
+
expect(result).toBeDefined();
|
|
177
|
+
// Should match sender-3 which has 'other.com' in allowedDomains
|
|
178
|
+
expect(result?.id).toBe('sender-3');
|
|
179
|
+
});
|
|
180
|
+
it('should fallback to priority when no domain match', () => {
|
|
181
|
+
const result = selector.selectBestSender(testSenders, 'unrelated.org', {
|
|
182
|
+
strategy: 'domain_match',
|
|
183
|
+
});
|
|
184
|
+
expect(result).toBeDefined();
|
|
185
|
+
expect(result?.priority).toBe(1); // Falls back to highest priority
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
describe('selectByPriority', () => {
|
|
189
|
+
it('should select sender with lowest priority number', () => {
|
|
190
|
+
const result = selector.selectBestSender(testSenders, 'example.com', {
|
|
191
|
+
strategy: 'priority',
|
|
192
|
+
});
|
|
193
|
+
expect(result?.priority).toBe(1);
|
|
194
|
+
expect(result?.id).toBe('sender-1');
|
|
195
|
+
});
|
|
196
|
+
it('should prefer verified senders with same priority', () => {
|
|
197
|
+
const candidates = [
|
|
198
|
+
{
|
|
199
|
+
...testSenders[0],
|
|
200
|
+
id: 'verified',
|
|
201
|
+
priority: 5,
|
|
202
|
+
isVerified: true,
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
...testSenders[1],
|
|
206
|
+
id: 'unverified',
|
|
207
|
+
priority: 5,
|
|
208
|
+
isVerified: false,
|
|
209
|
+
},
|
|
210
|
+
];
|
|
211
|
+
const result = selector.selectBestSender(candidates, 'example.com', {
|
|
212
|
+
strategy: 'priority',
|
|
213
|
+
});
|
|
214
|
+
expect(result?.id).toBe('verified');
|
|
215
|
+
expect(result?.isVerified).toBe(true);
|
|
216
|
+
});
|
|
217
|
+
it('should prefer more recently verified senders', () => {
|
|
218
|
+
const candidates = [
|
|
219
|
+
{
|
|
220
|
+
...testSenders[0],
|
|
221
|
+
id: 'newer',
|
|
222
|
+
priority: 5,
|
|
223
|
+
isVerified: true,
|
|
224
|
+
lastVerifiedAt: new Date('2025-01-15'),
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
...testSenders[1],
|
|
228
|
+
id: 'older',
|
|
229
|
+
priority: 5,
|
|
230
|
+
isVerified: true,
|
|
231
|
+
lastVerifiedAt: new Date('2025-01-10'),
|
|
232
|
+
},
|
|
233
|
+
];
|
|
234
|
+
const result = selector.selectBestSender(candidates, 'example.com', {
|
|
235
|
+
strategy: 'priority',
|
|
236
|
+
});
|
|
237
|
+
expect(result?.id).toBe('newer');
|
|
238
|
+
});
|
|
239
|
+
it('should fallback to creation order', () => {
|
|
240
|
+
const newer = { ...testSenders[0] };
|
|
241
|
+
delete newer.lastVerifiedAt;
|
|
242
|
+
const older = { ...testSenders[1] };
|
|
243
|
+
delete older.lastVerifiedAt;
|
|
244
|
+
const candidates = [
|
|
245
|
+
{
|
|
246
|
+
...newer,
|
|
247
|
+
id: 'newer',
|
|
248
|
+
priority: 5,
|
|
249
|
+
isVerified: true,
|
|
250
|
+
createdAt: new Date('2025-01-15'),
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
...older,
|
|
254
|
+
id: 'older',
|
|
255
|
+
priority: 5,
|
|
256
|
+
isVerified: true,
|
|
257
|
+
createdAt: new Date('2025-01-10'),
|
|
258
|
+
},
|
|
259
|
+
];
|
|
260
|
+
const result = selector.selectBestSender(candidates, 'example.com', {
|
|
261
|
+
strategy: 'priority',
|
|
262
|
+
});
|
|
263
|
+
expect(result?.id).toBe('older'); // Earlier creation date
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
describe('selectRoundRobin', () => {
|
|
267
|
+
it('should rotate through candidates', () => {
|
|
268
|
+
const candidates = [testSenders[0], testSenders[1], testSenders[2]];
|
|
269
|
+
const results = [
|
|
270
|
+
selector.selectBestSender(candidates, 'test.com', { strategy: 'round_robin' }),
|
|
271
|
+
selector.selectBestSender(candidates, 'test.com', { strategy: 'round_robin' }),
|
|
272
|
+
selector.selectBestSender(candidates, 'test.com', { strategy: 'round_robin' }),
|
|
273
|
+
selector.selectBestSender(candidates, 'test.com', { strategy: 'round_robin' }),
|
|
274
|
+
];
|
|
275
|
+
expect(results[0]?.id).toBe('sender-1');
|
|
276
|
+
expect(results[1]?.id).toBe('sender-2');
|
|
277
|
+
expect(results[2]?.id).toBe('sender-3');
|
|
278
|
+
expect(results[3]?.id).toBe('sender-1'); // Wraps around
|
|
279
|
+
});
|
|
280
|
+
it('should maintain separate counters per domain', () => {
|
|
281
|
+
const candidates = [testSenders[0], testSenders[1]];
|
|
282
|
+
const result1a = selector.selectBestSender(candidates, 'domain-a.com', {
|
|
283
|
+
strategy: 'round_robin',
|
|
284
|
+
});
|
|
285
|
+
const result2 = selector.selectBestSender(candidates, 'domain-b.com', {
|
|
286
|
+
strategy: 'round_robin',
|
|
287
|
+
});
|
|
288
|
+
const result1b = selector.selectBestSender(candidates, 'domain-a.com', {
|
|
289
|
+
strategy: 'round_robin',
|
|
290
|
+
});
|
|
291
|
+
expect(result1a?.id).toBe('sender-1');
|
|
292
|
+
expect(result2?.id).toBe('sender-1'); // Different domain, starts over
|
|
293
|
+
expect(result1b?.id).toBe('sender-2'); // Same domain, continues
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
describe('filterCandidates', () => {
|
|
297
|
+
it('should filter by verification status', () => {
|
|
298
|
+
const filtered = selector.filterCandidates(testSenders, {
|
|
299
|
+
allowUnverified: false,
|
|
300
|
+
});
|
|
301
|
+
expect(filtered.length).toBe(3); // Only verified senders
|
|
302
|
+
expect(filtered.every((s) => s.isVerified)).toBe(true);
|
|
303
|
+
});
|
|
304
|
+
it('should allow unverified when specified', () => {
|
|
305
|
+
const filtered = selector.filterCandidates(testSenders, {
|
|
306
|
+
allowUnverified: true,
|
|
307
|
+
});
|
|
308
|
+
expect(filtered.length).toBe(4); // All senders
|
|
309
|
+
});
|
|
310
|
+
it('should filter by provider', () => {
|
|
311
|
+
const filtered = selector.filterCandidates(testSenders, {
|
|
312
|
+
provider: 'sendgrid',
|
|
313
|
+
});
|
|
314
|
+
expect(filtered.length).toBe(3); // Only SendGrid senders
|
|
315
|
+
expect(filtered.every((s) => s.provider === 'sendgrid')).toBe(true);
|
|
316
|
+
});
|
|
317
|
+
it('should exclude specific sender IDs', () => {
|
|
318
|
+
const filtered = selector.filterCandidates(testSenders, {
|
|
319
|
+
excludeIds: ['sender-1', 'sender-3'],
|
|
320
|
+
});
|
|
321
|
+
expect(filtered.length).toBe(2);
|
|
322
|
+
expect(filtered.find((s) => s.id === 'sender-1')).toBeUndefined();
|
|
323
|
+
expect(filtered.find((s) => s.id === 'sender-3')).toBeUndefined();
|
|
324
|
+
});
|
|
325
|
+
it('should apply multiple filters', () => {
|
|
326
|
+
const filtered = selector.filterCandidates(testSenders, {
|
|
327
|
+
allowUnverified: false,
|
|
328
|
+
provider: 'sendgrid',
|
|
329
|
+
excludeIds: ['sender-1'],
|
|
330
|
+
});
|
|
331
|
+
expect(filtered.length).toBe(2); // Verified, SendGrid, not excluded
|
|
332
|
+
expect(filtered.every((s) => s.isVerified && s.provider === 'sendgrid')).toBe(true);
|
|
333
|
+
expect(filtered.find((s) => s.id === 'sender-1')).toBeUndefined();
|
|
334
|
+
});
|
|
335
|
+
it('should handle empty exclude list', () => {
|
|
336
|
+
const filtered = selector.filterCandidates(testSenders, {
|
|
337
|
+
excludeIds: [],
|
|
338
|
+
allowUnverified: true,
|
|
339
|
+
});
|
|
340
|
+
expect(filtered.length).toBe(4); // No exclusions
|
|
341
|
+
});
|
|
342
|
+
it('should return copy of candidates when no filters', () => {
|
|
343
|
+
const filtered = selector.filterCandidates(testSenders, {
|
|
344
|
+
allowUnverified: true,
|
|
345
|
+
});
|
|
346
|
+
expect(filtered.length).toBe(4);
|
|
347
|
+
expect(filtered).not.toBe(testSenders); // Different array instance
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
//# sourceMappingURL=selector.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selector.test.js","sourceRoot":"","sources":["../../../src/__tests__/selection/selector.test.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAEF,4DAA4D;AAC5D,6CAA6C;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAG7D,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,IAAI,QAAwB,CAAC;IAC7B,IAAI,WAAkC,CAAC;IAEvC,UAAU,CAAC,GAAG,EAAE;QACd,MAAM,MAAM,GAAoB;YAC9B,QAAQ,EAAE,cAAc;YACxB,iBAAiB,EAAE,IAAI;YACvB,mBAAmB,EAAE,EAAE;SACxB,CAAC;QAEF,QAAQ,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;QAEtC,sBAAsB;QACtB,WAAW,GAAG;YACZ;gBACE,EAAE,EAAE,UAAU;gBACd,IAAI,EAAE,gBAAgB;gBACtB,SAAS,EAAE,qBAAqB;gBAChC,QAAQ,EAAE,SAAS;gBACnB,QAAQ,EAAE,UAAU;gBACpB,UAAU,EAAE,IAAI;gBAChB,kBAAkB,EAAE,UAAU;gBAC9B,SAAS,EAAE,IAAI;gBACf,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,CAAC;gBACX,MAAM,EAAE,aAAa;gBACrB,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gBACjC,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gBACjC,SAAS,EAAE,OAAO;gBAClB,cAAc,EAAE,OAAO;gBACvB,cAAc,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;aACvC;YACD;gBACE,EAAE,EAAE,UAAU;gBACd,IAAI,EAAE,YAAY;gBAClB,SAAS,EAAE,yBAAyB;gBACpC,QAAQ,EAAE,KAAK;gBACf,QAAQ,EAAE,UAAU;gBACpB,UAAU,EAAE,IAAI;gBAChB,kBAAkB,EAAE,UAAU;gBAC9B,SAAS,EAAE,KAAK;gBAChB,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,EAAE;gBACZ,MAAM,EAAE,iBAAiB;gBACzB,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gBACjC,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gBACjC,SAAS,EAAE,OAAO;gBAClB,cAAc,EAAE,OAAO;gBACvB,cAAc,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;aACvC;YACD;gBACE,EAAE,EAAE,UAAU;gBACd,IAAI,EAAE,kBAAkB;gBACxB,SAAS,EAAE,uBAAuB;gBAClC,QAAQ,EAAE,WAAW;gBACrB,QAAQ,EAAE,SAAS;gBACnB,UAAU,EAAE,KAAK;gBACjB,kBAAkB,EAAE,SAAS;gBAC7B,SAAS,EAAE,KAAK;gBAChB,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,CAAC;gBACX,MAAM,EAAE,aAAa;gBACrB,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gBACjC,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gBACjC,SAAS,EAAE,OAAO;gBAClB,cAAc,EAAE,OAAO;gBACvB,cAAc,EAAE,CAAC,eAAe,EAAE,WAAW,CAAC;aAC/C;YACD;gBACE,EAAE,EAAE,UAAU;gBACd,IAAI,EAAE,gBAAgB;gBACtB,SAAS,EAAE,qBAAqB;gBAChC,QAAQ,EAAE,SAAS;gBACnB,QAAQ,EAAE,UAAU;gBACpB,UAAU,EAAE,IAAI;gBAChB,kBAAkB,EAAE,UAAU;gBAC9B,SAAS,EAAE,KAAK;gBAChB,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,CAAC;gBACX,MAAM,EAAE,aAAa;gBACrB,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gBACjC,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;gBACjC,SAAS,EAAE,OAAO;gBAClB,cAAc,EAAE,OAAO;gBACvB,cAAc,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;aACvC;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YACjD,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;YAC5D,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,aAAa,EAAE;gBACnE,QAAQ,EAAE,cAAc;aACzB,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YAC7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,mCAAmC;QACvE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC5C,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,aAAa,EAAE;gBACnE,QAAQ,EAAE,UAAU;aACrB,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YAC7B,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;YAC/C,MAAM,UAAU,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;YAEpD,MAAM,OAAO,GAAG,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,aAAa,EAAE;gBACnE,QAAQ,EAAE,aAAa;aACxB,CAAC,CAAC;YACH,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAErC,MAAM,OAAO,GAAG,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,aAAa,EAAE;gBACnE,QAAQ,EAAE,aAAa;aACxB,CAAC,CAAC;YACH,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAErC,MAAM,OAAO,GAAG,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,aAAa,EAAE;gBACnE,QAAQ,EAAE,aAAa;aACxB,CAAC,CAAC;YACH,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,eAAe;QACvD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YACjD,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;YACrE,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YAC7B,mCAAmC;YACnC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;YACnD,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,aAAa,EAAE;gBACnE,8DAA8D;gBAC9D,QAAQ,EAAE,SAAgB;aAC3B,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YAC7B,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,0BAA0B;QACjE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;YACxC,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,aAAa,EAAE;gBACnE,QAAQ,EAAE,cAAc;aACzB,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YAC7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,uCAAuC;QAC3E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;YACrC,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,iBAAiB,EAAE;gBACvE,QAAQ,EAAE,cAAc;aACzB,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YAC7B,yCAAyC;YACzC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;YACzC,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,iBAAiB,EAAE;gBACvE,QAAQ,EAAE,cAAc;aACzB,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YAC7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,oBAAoB;QACtE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;YACrD,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,WAAW,EAAE;gBACjE,QAAQ,EAAE,cAAc;aACzB,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YAC7B,gEAAgE;YAChE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;YAC1D,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,eAAe,EAAE;gBACrE,QAAQ,EAAE,cAAc;aACzB,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YAC7B,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,iCAAiC;QACrE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;YAC1D,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,aAAa,EAAE;gBACnE,QAAQ,EAAE,UAAU;aACrB,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;YAC3D,MAAM,UAAU,GAA0B;gBACxC;oBACE,GAAG,WAAW,CAAC,CAAC,CAAC;oBACjB,EAAE,EAAE,UAAU;oBACd,QAAQ,EAAE,CAAC;oBACX,UAAU,EAAE,IAAI;iBACjB;gBACD;oBACE,GAAG,WAAW,CAAC,CAAC,CAAC;oBACjB,EAAE,EAAE,YAAY;oBAChB,QAAQ,EAAE,CAAC;oBACX,UAAU,EAAE,KAAK;iBAClB;aACF,CAAC;YAEF,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,aAAa,EAAE;gBAClE,QAAQ,EAAE,UAAU;aACrB,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACpC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,UAAU,GAA0B;gBACxC;oBACE,GAAG,WAAW,CAAC,CAAC,CAAC;oBACjB,EAAE,EAAE,OAAO;oBACX,QAAQ,EAAE,CAAC;oBACX,UAAU,EAAE,IAAI;oBAChB,cAAc,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;iBACvC;gBACD;oBACE,GAAG,WAAW,CAAC,CAAC,CAAC;oBACjB,EAAE,EAAE,OAAO;oBACX,QAAQ,EAAE,CAAC;oBACX,UAAU,EAAE,IAAI;oBAChB,cAAc,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;iBACvC;aACF,CAAC;YAEF,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,aAAa,EAAE;gBAClE,QAAQ,EAAE,UAAU;aACrB,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;YAC3C,MAAM,KAAK,GAAG,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;YACpC,OAAO,KAAK,CAAC,cAAc,CAAC;YAC5B,MAAM,KAAK,GAAG,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;YACpC,OAAO,KAAK,CAAC,cAAc,CAAC;YAE5B,MAAM,UAAU,GAA0B;gBACxC;oBACE,GAAG,KAAK;oBACR,EAAE,EAAE,OAAO;oBACX,QAAQ,EAAE,CAAC;oBACX,UAAU,EAAE,IAAI;oBAChB,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;iBAClC;gBACD;oBACE,GAAG,KAAK;oBACR,EAAE,EAAE,OAAO;oBACX,QAAQ,EAAE,CAAC;oBACX,UAAU,EAAE,IAAI;oBAChB,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC;iBAClC;aACF,CAAC;YAEF,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,aAAa,EAAE;gBAClE,QAAQ,EAAE,UAAU;aACrB,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB;QAC5D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC1C,MAAM,UAAU,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;YAEpE,MAAM,OAAO,GAAG;gBACd,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;gBAC9E,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;gBAC9E,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;gBAC9E,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;aAC/E,CAAC;YAEF,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACxC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACxC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACxC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,eAAe;QAC1D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,UAAU,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;YAEpD,MAAM,QAAQ,GAAG,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,cAAc,EAAE;gBACrE,QAAQ,EAAE,aAAa;aACxB,CAAC,CAAC;YACH,MAAM,OAAO,GAAG,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,cAAc,EAAE;gBACpE,QAAQ,EAAE,aAAa;aACxB,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,cAAc,EAAE;gBACrE,QAAQ,EAAE,aAAa;aACxB,CAAC,CAAC;YAEH,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACtC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,gCAAgC;YACtE,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,yBAAyB;QAClE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;YAC9C,MAAM,QAAQ,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE;gBACtD,eAAe,EAAE,KAAK;aACvB,CAAC,CAAC;YAEH,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB;YACzD,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,QAAQ,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE;gBACtD,eAAe,EAAE,IAAI;aACtB,CAAC,CAAC;YAEH,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc;QACjD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;YACnC,MAAM,QAAQ,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE;gBACtD,QAAQ,EAAE,UAAU;aACrB,CAAC,CAAC;YAEH,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB;YACzD,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC5C,MAAM,QAAQ,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE;gBACtD,UAAU,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC;aACrC,CAAC,CAAC;YAEH,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;YAClE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACpE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;YACvC,MAAM,QAAQ,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE;gBACtD,eAAe,EAAE,KAAK;gBACtB,QAAQ,EAAE,UAAU;gBACpB,UAAU,EAAE,CAAC,UAAU,CAAC;aACzB,CAAC,CAAC;YAEH,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,mCAAmC;YACpE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpF,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACpE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC1C,MAAM,QAAQ,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE;gBACtD,UAAU,EAAE,EAAE;gBACd,eAAe,EAAE,IAAI;aACtB,CAAC,CAAC;YAEH,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB;QACnD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;YAC1D,MAAM,QAAQ,GAAG,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE;gBACtD,eAAe,EAAE,IAAI;aACtB,CAAC,CAAC;YAEH,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,2BAA2B;QACrE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-utils.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/utils/domain-utils.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2025 Bernier LLC
|
|
3
|
+
|
|
4
|
+
This file is licensed to the client under a limited-use license.
|
|
5
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
6
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
7
|
+
*/
|
|
8
|
+
// Test framework functions are globally available via Jest
|
|
9
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
10
|
+
/* global describe, it, expect */
|
|
11
|
+
import { extractDomain, isDomainMatch, isValidEmail, getParentDomain, isSubdomainOf, } from '../../utils/domain-utils.js';
|
|
12
|
+
describe('domain-utils', () => {
|
|
13
|
+
describe('extractDomain', () => {
|
|
14
|
+
it('should extract domain from valid email', () => {
|
|
15
|
+
expect(extractDomain('user@example.com')).toBe('example.com');
|
|
16
|
+
expect(extractDomain('admin@app.example.com')).toBe('app.example.com');
|
|
17
|
+
expect(extractDomain('test@sub.domain.example.org')).toBe('sub.domain.example.org');
|
|
18
|
+
});
|
|
19
|
+
it('should convert domain to lowercase', () => {
|
|
20
|
+
expect(extractDomain('User@Example.COM')).toBe('example.com');
|
|
21
|
+
expect(extractDomain('ADMIN@APP.EXAMPLE.COM')).toBe('app.example.com');
|
|
22
|
+
});
|
|
23
|
+
it('should throw error for invalid email format', () => {
|
|
24
|
+
expect(() => extractDomain('invalid-email')).toThrow('Invalid email format');
|
|
25
|
+
expect(() => extractDomain('no-at-sign')).toThrow('Invalid email format');
|
|
26
|
+
expect(() => extractDomain('@example.com')).toThrow('Invalid email format');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe('isDomainMatch', () => {
|
|
30
|
+
it('should match exact domains', () => {
|
|
31
|
+
expect(isDomainMatch('example.com', 'example.com')).toBe(true);
|
|
32
|
+
expect(isDomainMatch('Example.COM', 'example.com')).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
it('should match subdomains', () => {
|
|
35
|
+
expect(isDomainMatch('app.example.com', 'example.com')).toBe(true);
|
|
36
|
+
expect(isDomainMatch('api.app.example.com', 'app.example.com')).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
it('should match parent domains', () => {
|
|
39
|
+
expect(isDomainMatch('example.com', 'app.example.com')).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
it('should support wildcard patterns', () => {
|
|
42
|
+
expect(isDomainMatch('app.example.com', '*.example.com')).toBe(true);
|
|
43
|
+
expect(isDomainMatch('api.example.com', '*.example.com')).toBe(true);
|
|
44
|
+
expect(isDomainMatch('example.com', '*.example.com')).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
it('should not match unrelated domains', () => {
|
|
47
|
+
expect(isDomainMatch('example.com', 'other.com')).toBe(false);
|
|
48
|
+
expect(isDomainMatch('app.example.com', 'other.com')).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('isValidEmail', () => {
|
|
52
|
+
it('should validate correct email addresses', () => {
|
|
53
|
+
expect(isValidEmail('user@example.com')).toBe(true);
|
|
54
|
+
expect(isValidEmail('admin.test@app.example.org')).toBe(true);
|
|
55
|
+
expect(isValidEmail('user+tag@example.co.uk')).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
it('should reject invalid email addresses', () => {
|
|
58
|
+
expect(isValidEmail('invalid')).toBe(false);
|
|
59
|
+
expect(isValidEmail('no-at-sign')).toBe(false);
|
|
60
|
+
expect(isValidEmail('@example.com')).toBe(false);
|
|
61
|
+
expect(isValidEmail('user@')).toBe(false);
|
|
62
|
+
expect(isValidEmail('user @example.com')).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe('getParentDomain', () => {
|
|
66
|
+
it('should get parent domain from subdomain', () => {
|
|
67
|
+
expect(getParentDomain('app.example.com')).toBe('example.com');
|
|
68
|
+
expect(getParentDomain('api.app.example.com')).toBe('app.example.com');
|
|
69
|
+
});
|
|
70
|
+
it('should return null for top-level domains', () => {
|
|
71
|
+
expect(getParentDomain('example.com')).toBe(null);
|
|
72
|
+
expect(getParentDomain('example.org')).toBe(null);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
describe('isSubdomainOf', () => {
|
|
76
|
+
it('should identify subdomains correctly', () => {
|
|
77
|
+
expect(isSubdomainOf('app.example.com', 'example.com')).toBe(true);
|
|
78
|
+
expect(isSubdomainOf('api.app.example.com', 'example.com')).toBe(true);
|
|
79
|
+
expect(isSubdomainOf('api.app.example.com', 'app.example.com')).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
it('should be case insensitive', () => {
|
|
82
|
+
expect(isSubdomainOf('APP.Example.COM', 'example.com')).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
it('should return false for non-subdomains', () => {
|
|
85
|
+
expect(isSubdomainOf('example.com', 'example.com')).toBe(false);
|
|
86
|
+
expect(isSubdomainOf('example.com', 'app.example.com')).toBe(false);
|
|
87
|
+
expect(isSubdomainOf('other.com', 'example.com')).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=domain-utils.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-utils.test.js","sourceRoot":"","sources":["../../../src/__tests__/utils/domain-utils.test.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAEF,2DAA2D;AAC3D,4DAA4D;AAC5D,iCAAiC;AACjC,OAAO,EACL,aAAa,EACb,aAAa,EACb,YAAY,EACZ,eAAe,EACf,aAAa,GACd,MAAM,6BAA6B,CAAC;AAErC,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC9D,MAAM,CAAC,aAAa,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YACvE,MAAM,CAAC,aAAa,CAAC,6BAA6B,CAAC,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QACtF,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC5C,MAAM,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC9D,MAAM,CAAC,aAAa,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACzE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;YACrD,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;YAC7E,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;YAC1E,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;QAC9E,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;YACpC,MAAM,CAAC,aAAa,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC/D,MAAM,CAAC,aAAa,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;YACjC,MAAM,CAAC,aAAa,CAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnE,MAAM,CAAC,aAAa,CAAC,qBAAqB,EAAE,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;YACrC,MAAM,CAAC,aAAa,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC1C,MAAM,CAAC,aAAa,CAAC,iBAAiB,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrE,MAAM,CAAC,aAAa,CAAC,iBAAiB,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrE,MAAM,CAAC,aAAa,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC5C,MAAM,CAAC,aAAa,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC9D,MAAM,CAAC,aAAa,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YACjD,MAAM,CAAC,YAAY,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpD,MAAM,CAAC,YAAY,CAAC,4BAA4B,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9D,MAAM,CAAC,YAAY,CAAC,wBAAwB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;YAC/C,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC5C,MAAM,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC/C,MAAM,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjD,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1C,MAAM,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YACjD,MAAM,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC/D,MAAM,CAAC,eAAe,CAAC,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACzE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;YAClD,MAAM,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;YAC9C,MAAM,CAAC,aAAa,CAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnE,MAAM,CAAC,aAAa,CAAC,qBAAqB,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvE,MAAM,CAAC,aAAa,CAAC,qBAAqB,EAAE,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;YACpC,MAAM,CAAC,aAAa,CAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,CAAC,aAAa,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAChE,MAAM,CAAC,aAAa,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACpE,MAAM,CAAC,aAAa,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-utils.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/utils/validation-utils.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2025 Bernier LLC
|
|
3
|
+
|
|
4
|
+
This file is licensed to the client under a limited-use license.
|
|
5
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
6
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
7
|
+
*/
|
|
8
|
+
import { validateCreateSenderRequest, validateDomainAllowed, validateSenderConfiguration, } from '../../utils/validation-utils.js';
|
|
9
|
+
describe('validation-utils', () => {
|
|
10
|
+
describe('validateCreateSenderRequest', () => {
|
|
11
|
+
const validRequest = {
|
|
12
|
+
name: 'Test Sender',
|
|
13
|
+
fromEmail: 'test@example.com',
|
|
14
|
+
fromName: 'Test User',
|
|
15
|
+
provider: 'sendgrid',
|
|
16
|
+
createdBy: 'user123',
|
|
17
|
+
};
|
|
18
|
+
it('should validate a correct request', () => {
|
|
19
|
+
const result = validateCreateSenderRequest(validRequest);
|
|
20
|
+
expect(result.errors).toHaveLength(0);
|
|
21
|
+
});
|
|
22
|
+
it('should detect missing required fields', () => {
|
|
23
|
+
const invalidRequest = {
|
|
24
|
+
...validRequest,
|
|
25
|
+
name: '',
|
|
26
|
+
};
|
|
27
|
+
const result = validateCreateSenderRequest(invalidRequest);
|
|
28
|
+
expect(result.errors.length).toBeGreaterThan(0);
|
|
29
|
+
expect(result.errors.some((e) => e.field === 'name')).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
it('should detect invalid email format', () => {
|
|
32
|
+
const invalidRequest = {
|
|
33
|
+
...validRequest,
|
|
34
|
+
fromEmail: 'invalid-email',
|
|
35
|
+
};
|
|
36
|
+
const result = validateCreateSenderRequest(invalidRequest);
|
|
37
|
+
expect(result.errors.some((e) => e.field === 'fromEmail')).toBe(true);
|
|
38
|
+
expect(result.errors.some((e) => e.code === 'INVALID_FORMAT')).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
it('should detect invalid reply-to email', () => {
|
|
41
|
+
const invalidRequest = {
|
|
42
|
+
...validRequest,
|
|
43
|
+
replyToEmail: 'invalid',
|
|
44
|
+
};
|
|
45
|
+
const result = validateCreateSenderRequest(invalidRequest);
|
|
46
|
+
expect(result.errors.some((e) => e.field === 'replyToEmail')).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
it('should detect invalid priority', () => {
|
|
49
|
+
const invalidRequest = {
|
|
50
|
+
...validRequest,
|
|
51
|
+
priority: -1,
|
|
52
|
+
};
|
|
53
|
+
const result = validateCreateSenderRequest(invalidRequest);
|
|
54
|
+
expect(result.errors.some((e) => e.field === 'priority')).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
it('should warn about empty allowed domains', () => {
|
|
57
|
+
const requestWithEmptyDomains = {
|
|
58
|
+
...validRequest,
|
|
59
|
+
allowedDomains: [],
|
|
60
|
+
};
|
|
61
|
+
const result = validateCreateSenderRequest(requestWithEmptyDomains);
|
|
62
|
+
expect(result.warnings.some((w) => w.field === 'allowedDomains')).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
it('should warn about missing reply-to email', () => {
|
|
65
|
+
const result = validateCreateSenderRequest(validRequest);
|
|
66
|
+
expect(result.warnings.some((w) => w.field === 'replyToEmail')).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe('validateDomainAllowed', () => {
|
|
70
|
+
it('should allow any domain when no restrictions', () => {
|
|
71
|
+
const result = validateDomainAllowed('test@example.com', undefined);
|
|
72
|
+
expect(result.allowed).toBe(true);
|
|
73
|
+
const result2 = validateDomainAllowed('test@example.com', []);
|
|
74
|
+
expect(result2.allowed).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
it('should allow exact domain match', () => {
|
|
77
|
+
const result = validateDomainAllowed('test@example.com', ['example.com']);
|
|
78
|
+
expect(result.allowed).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
it('should allow subdomain match', () => {
|
|
81
|
+
const result = validateDomainAllowed('test@app.example.com', ['example.com']);
|
|
82
|
+
expect(result.allowed).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
it('should reject non-matching domain', () => {
|
|
85
|
+
const result = validateDomainAllowed('test@other.com', ['example.com']);
|
|
86
|
+
expect(result.allowed).toBe(false);
|
|
87
|
+
expect(result.reason).toContain('not in allowed domains');
|
|
88
|
+
});
|
|
89
|
+
it('should handle invalid email format', () => {
|
|
90
|
+
const result = validateDomainAllowed('invalid-email', ['example.com']);
|
|
91
|
+
expect(result.allowed).toBe(false);
|
|
92
|
+
expect(result.reason).toContain('Invalid email format');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
describe('validateSenderConfiguration', () => {
|
|
96
|
+
it('should validate correct configuration', () => {
|
|
97
|
+
const result = validateSenderConfiguration('test@example.com', ['example.com']);
|
|
98
|
+
expect(result.valid).toBe(true);
|
|
99
|
+
expect(result.errors).toHaveLength(0);
|
|
100
|
+
});
|
|
101
|
+
it('should detect invalid email format', () => {
|
|
102
|
+
const result = validateSenderConfiguration('invalid', ['example.com']);
|
|
103
|
+
expect(result.valid).toBe(false);
|
|
104
|
+
expect(result.errors.some((e) => e.code === 'INVALID_FORMAT')).toBe(true);
|
|
105
|
+
});
|
|
106
|
+
it('should detect domain not allowed', () => {
|
|
107
|
+
const result = validateSenderConfiguration('test@other.com', ['example.com']);
|
|
108
|
+
expect(result.valid).toBe(false);
|
|
109
|
+
expect(result.errors.some((e) => e.code === 'DOMAIN_NOT_ALLOWED')).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
it('should allow any domain when no restrictions', () => {
|
|
112
|
+
const result = validateSenderConfiguration('test@anything.com', undefined);
|
|
113
|
+
expect(result.valid).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
//# sourceMappingURL=validation-utils.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-utils.test.js","sourceRoot":"","sources":["../../../src/__tests__/utils/validation-utils.test.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAMF,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,2BAA2B,GAC5B,MAAM,iCAAiC,CAAC;AAEzC,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;QAC3C,MAAM,YAAY,GAAwB;YACxC,IAAI,EAAE,aAAa;YACnB,SAAS,EAAE,kBAAkB;YAC7B,QAAQ,EAAE,WAAW;YACrB,QAAQ,EAAE,UAAU;YACpB,SAAS,EAAE,SAAS;SACrB,CAAC;QAEF,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;YAC3C,MAAM,MAAM,GAAG,2BAA2B,CAAC,YAAY,CAAC,CAAC;YACzD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;YAC/C,MAAM,cAAc,GAAG;gBACrB,GAAG,YAAY;gBACf,IAAI,EAAE,EAAE;aACT,CAAC;YACF,MAAM,MAAM,GAAG,2BAA2B,CAAC,cAAc,CAAC,CAAC;YAC3D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;YAChD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC5C,MAAM,cAAc,GAAG;gBACrB,GAAG,YAAY;gBACf,SAAS,EAAE,eAAe;aAC3B,CAAC;YACF,MAAM,MAAM,GAAG,2BAA2B,CAAC,cAAc,CAAC,CAAC;YAC3D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;YAC9C,MAAM,cAAc,GAAG;gBACrB,GAAG,YAAY;gBACf,YAAY,EAAE,SAAS;aACxB,CAAC;YACF,MAAM,MAAM,GAAG,2BAA2B,CAAC,cAAc,CAAC,CAAC;YAC3D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;YACxC,MAAM,cAAc,GAAG;gBACrB,GAAG,YAAY;gBACf,QAAQ,EAAE,CAAC,CAAC;aACb,CAAC;YACF,MAAM,MAAM,GAAG,2BAA2B,CAAC,cAAc,CAAC,CAAC;YAC3D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YACjD,MAAM,uBAAuB,GAAG;gBAC9B,GAAG,YAAY;gBACf,cAAc,EAAE,EAAE;aACnB,CAAC;YACF,MAAM,MAAM,GAAG,2BAA2B,CAAC,uBAAuB,CAAC,CAAC;YACpE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;YAClD,MAAM,MAAM,GAAG,2BAA2B,CAAC,YAAY,CAAC,CAAC;YACzD,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7E,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,MAAM,GAAG,qBAAqB,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;YACpE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAElC,MAAM,OAAO,GAAG,qBAAqB,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;YAC9D,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;YACzC,MAAM,MAAM,GAAG,qBAAqB,CAAC,kBAAkB,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC;YAC1E,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;YACtC,MAAM,MAAM,GAAG,qBAAqB,CAAC,sBAAsB,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC;YAC9E,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;YAC3C,MAAM,MAAM,GAAG,qBAAqB,CAAC,gBAAgB,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC;YACxE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC5C,MAAM,MAAM,GAAG,qBAAqB,CAAC,eAAe,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC;YACvE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;QAC3C,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;YAC/C,MAAM,MAAM,GAAG,2BAA2B,CAAC,kBAAkB,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC;YAChF,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAChC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC5C,MAAM,MAAM,GAAG,2BAA2B,CAAC,SAAS,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC;YACvE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC1C,MAAM,MAAM,GAAG,2BAA2B,CAAC,gBAAgB,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC;YAC9E,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChF,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,MAAM,GAAG,2BAA2B,CAAC,mBAAmB,EAAE,SAAS,CAAC,CAAC;YAC3E,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|