@bernierllc/email-webhook-events 1.0.0
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/.eslintrc.cjs +29 -0
- package/README.md +349 -0
- package/__tests__/EmailWebhookEventsService.test.ts +247 -0
- package/__tests__/analytics/AnalyticsEngine.test.ts +244 -0
- package/__tests__/normalizers/MailgunNormalizer.test.ts +149 -0
- package/__tests__/normalizers/PostmarkNormalizer.test.ts +112 -0
- package/__tests__/normalizers/SESNormalizer.test.ts +168 -0
- package/__tests__/normalizers/SMTP2GONormalizer.test.ts +83 -0
- package/__tests__/normalizers/SendGridNormalizer.test.ts +181 -0
- package/__tests__/persistence/PersistenceAdapter.test.ts +103 -0
- package/coverage/clover.xml +328 -0
- package/coverage/coverage-final.json +10 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +161 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/src/EmailWebhookEventsService.ts.html +826 -0
- package/coverage/lcov-report/src/analytics/AnalyticsEngine.ts.html +775 -0
- package/coverage/lcov-report/src/analytics/index.html +116 -0
- package/coverage/lcov-report/src/index.html +131 -0
- package/coverage/lcov-report/src/normalizers/MailgunNormalizer.ts.html +301 -0
- package/coverage/lcov-report/src/normalizers/PostmarkNormalizer.ts.html +301 -0
- package/coverage/lcov-report/src/normalizers/SESNormalizer.ts.html +436 -0
- package/coverage/lcov-report/src/normalizers/SMTP2GONormalizer.ts.html +247 -0
- package/coverage/lcov-report/src/normalizers/SendGridNormalizer.ts.html +274 -0
- package/coverage/lcov-report/src/normalizers/index.html +176 -0
- package/coverage/lcov-report/src/persistence/InMemoryPersistenceAdapter.ts.html +289 -0
- package/coverage/lcov-report/src/persistence/index.html +116 -0
- package/coverage/lcov-report/src/types.ts.html +823 -0
- package/coverage/lcov.info +710 -0
- package/dist/EmailWebhookEventsService.d.ts +53 -0
- package/dist/EmailWebhookEventsService.d.ts.map +1 -0
- package/dist/EmailWebhookEventsService.js +198 -0
- package/dist/EmailWebhookEventsService.js.map +1 -0
- package/dist/analytics/AnalyticsEngine.d.ts +20 -0
- package/dist/analytics/AnalyticsEngine.d.ts.map +1 -0
- package/dist/analytics/AnalyticsEngine.js +160 -0
- package/dist/analytics/AnalyticsEngine.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/normalizers/EventNormalizer.d.ts +5 -0
- package/dist/normalizers/EventNormalizer.d.ts.map +1 -0
- package/dist/normalizers/EventNormalizer.js +10 -0
- package/dist/normalizers/EventNormalizer.js.map +1 -0
- package/dist/normalizers/MailgunNormalizer.d.ts +9 -0
- package/dist/normalizers/MailgunNormalizer.d.ts.map +1 -0
- package/dist/normalizers/MailgunNormalizer.js +73 -0
- package/dist/normalizers/MailgunNormalizer.js.map +1 -0
- package/dist/normalizers/PostmarkNormalizer.d.ts +10 -0
- package/dist/normalizers/PostmarkNormalizer.d.ts.map +1 -0
- package/dist/normalizers/PostmarkNormalizer.js +75 -0
- package/dist/normalizers/PostmarkNormalizer.js.map +1 -0
- package/dist/normalizers/SESNormalizer.d.ts +16 -0
- package/dist/normalizers/SESNormalizer.d.ts.map +1 -0
- package/dist/normalizers/SESNormalizer.js +107 -0
- package/dist/normalizers/SESNormalizer.js.map +1 -0
- package/dist/normalizers/SMTP2GONormalizer.d.ts +9 -0
- package/dist/normalizers/SMTP2GONormalizer.d.ts.map +1 -0
- package/dist/normalizers/SMTP2GONormalizer.js +55 -0
- package/dist/normalizers/SMTP2GONormalizer.js.map +1 -0
- package/dist/normalizers/SendGridNormalizer.d.ts +9 -0
- package/dist/normalizers/SendGridNormalizer.d.ts.map +1 -0
- package/dist/normalizers/SendGridNormalizer.js +64 -0
- package/dist/normalizers/SendGridNormalizer.js.map +1 -0
- package/dist/persistence/InMemoryPersistenceAdapter.d.ts +12 -0
- package/dist/persistence/InMemoryPersistenceAdapter.d.ts.map +1 -0
- package/dist/persistence/InMemoryPersistenceAdapter.js +58 -0
- package/dist/persistence/InMemoryPersistenceAdapter.js.map +1 -0
- package/dist/persistence/PersistenceAdapter.d.ts +7 -0
- package/dist/persistence/PersistenceAdapter.d.ts.map +1 -0
- package/dist/persistence/PersistenceAdapter.js +10 -0
- package/dist/persistence/PersistenceAdapter.js.map +1 -0
- package/dist/types.d.ts +178 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +41 -0
- package/dist/types.js.map +1 -0
- package/jest.config.cjs +29 -0
- package/package.json +51 -0
- package/src/EmailWebhookEventsService.ts +247 -0
- package/src/analytics/AnalyticsEngine.ts +230 -0
- package/src/index.ts +39 -0
- package/src/normalizers/EventNormalizer.ts +13 -0
- package/src/normalizers/MailgunNormalizer.ts +72 -0
- package/src/normalizers/PostmarkNormalizer.ts +72 -0
- package/src/normalizers/SESNormalizer.ts +117 -0
- package/src/normalizers/SMTP2GONormalizer.ts +54 -0
- package/src/normalizers/SendGridNormalizer.ts +63 -0
- package/src/persistence/InMemoryPersistenceAdapter.ts +68 -0
- package/src/persistence/PersistenceAdapter.ts +15 -0
- package/src/types.ts +246 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,83 @@
|
|
|
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
|
+
|
|
9
|
+
import { SMTP2GONormalizer } from '../../src/normalizers/SMTP2GONormalizer';
|
|
10
|
+
import { EmailEventType, EmailProvider, WebhookPayload } from '../../src/types';
|
|
11
|
+
|
|
12
|
+
describe('SMTP2GONormalizer', () => {
|
|
13
|
+
let normalizer: SMTP2GONormalizer;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
normalizer = new SMTP2GONormalizer();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should normalize delivered event', () => {
|
|
20
|
+
const webhook: WebhookPayload = {
|
|
21
|
+
provider: EmailProvider.SMTP2GO,
|
|
22
|
+
signature: 'test-signature',
|
|
23
|
+
payload: {
|
|
24
|
+
event: 'delivered',
|
|
25
|
+
message_id: 'msg-123',
|
|
26
|
+
email_id: 'email-456',
|
|
27
|
+
rcpt_to: 'user@example.com',
|
|
28
|
+
timestamp: '2025-01-01T00:00:00.000Z'
|
|
29
|
+
},
|
|
30
|
+
headers: {}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const result = normalizer.normalize(webhook);
|
|
34
|
+
|
|
35
|
+
expect(result.type).toBe(EmailEventType.DELIVERED);
|
|
36
|
+
expect(result.recipient).toBe('user@example.com');
|
|
37
|
+
expect(result.provider).toBe(EmailProvider.SMTP2GO);
|
|
38
|
+
expect(result.messageId).toBe('msg-123');
|
|
39
|
+
expect(result.emailId).toBe('email-456');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should normalize bounce event with hard bounce', () => {
|
|
43
|
+
const webhook: WebhookPayload = {
|
|
44
|
+
provider: EmailProvider.SMTP2GO,
|
|
45
|
+
signature: 'test-signature',
|
|
46
|
+
payload: {
|
|
47
|
+
event: 'bounce',
|
|
48
|
+
message_id: 'msg-123',
|
|
49
|
+
rcpt_to: 'user@example.com',
|
|
50
|
+
timestamp: '2025-01-01T00:00:00.000Z',
|
|
51
|
+
bounce_type: 'hard bounce',
|
|
52
|
+
reason: 'User unknown'
|
|
53
|
+
},
|
|
54
|
+
headers: {}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const result = normalizer.normalize(webhook);
|
|
58
|
+
|
|
59
|
+
expect(result.type).toBe(EmailEventType.BOUNCED);
|
|
60
|
+
expect(result.metadata.bounceType).toBe('hard');
|
|
61
|
+
expect(result.metadata.bounceReason).toBe('User unknown');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should normalize failed event', () => {
|
|
65
|
+
const webhook: WebhookPayload = {
|
|
66
|
+
provider: EmailProvider.SMTP2GO,
|
|
67
|
+
signature: 'test-signature',
|
|
68
|
+
payload: {
|
|
69
|
+
event: 'failed',
|
|
70
|
+
message_id: 'msg-123',
|
|
71
|
+
rcpt_to: 'user@example.com',
|
|
72
|
+
timestamp: '2025-01-01T00:00:00.000Z',
|
|
73
|
+
reason: 'SMTP error'
|
|
74
|
+
},
|
|
75
|
+
headers: {}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const result = normalizer.normalize(webhook);
|
|
79
|
+
|
|
80
|
+
expect(result.type).toBe(EmailEventType.FAILED);
|
|
81
|
+
expect(result.metadata.failureReason).toBe('SMTP error');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -0,0 +1,181 @@
|
|
|
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
|
+
|
|
9
|
+
import { SendGridNormalizer } from '../../src/normalizers/SendGridNormalizer';
|
|
10
|
+
import { EmailEventType, EmailProvider, WebhookPayload } from '../../src/types';
|
|
11
|
+
|
|
12
|
+
describe('SendGridNormalizer', () => {
|
|
13
|
+
let normalizer: SendGridNormalizer;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
normalizer = new SendGridNormalizer();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should normalize delivered event', () => {
|
|
20
|
+
const webhook: WebhookPayload = {
|
|
21
|
+
provider: EmailProvider.SENDGRID,
|
|
22
|
+
signature: 'test-signature',
|
|
23
|
+
payload: {
|
|
24
|
+
event: 'delivered',
|
|
25
|
+
email: 'user@example.com',
|
|
26
|
+
timestamp: 1234567890,
|
|
27
|
+
sg_message_id: 'msg-123',
|
|
28
|
+
email_id: 'email-456'
|
|
29
|
+
},
|
|
30
|
+
headers: {}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const result = normalizer.normalize(webhook);
|
|
34
|
+
|
|
35
|
+
expect(result.type).toBe(EmailEventType.DELIVERED);
|
|
36
|
+
expect(result.recipient).toBe('user@example.com');
|
|
37
|
+
expect(result.provider).toBe(EmailProvider.SENDGRID);
|
|
38
|
+
expect(result.messageId).toBe('msg-123');
|
|
39
|
+
expect(result.emailId).toBe('email-456');
|
|
40
|
+
expect(result.timestamp).toEqual(new Date(1234567890000));
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should normalize open event', () => {
|
|
44
|
+
const webhook: WebhookPayload = {
|
|
45
|
+
provider: EmailProvider.SENDGRID,
|
|
46
|
+
signature: 'test-signature',
|
|
47
|
+
payload: {
|
|
48
|
+
event: 'open',
|
|
49
|
+
email: 'user@example.com',
|
|
50
|
+
timestamp: 1234567890,
|
|
51
|
+
sg_message_id: 'msg-123',
|
|
52
|
+
useragent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) mobile'
|
|
53
|
+
},
|
|
54
|
+
headers: {}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const result = normalizer.normalize(webhook);
|
|
58
|
+
|
|
59
|
+
expect(result.type).toBe(EmailEventType.OPENED);
|
|
60
|
+
expect(result.metadata.deviceType).toBe('mobile');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should normalize click event with URL', () => {
|
|
64
|
+
const webhook: WebhookPayload = {
|
|
65
|
+
provider: EmailProvider.SENDGRID,
|
|
66
|
+
signature: 'test-signature',
|
|
67
|
+
payload: {
|
|
68
|
+
event: 'click',
|
|
69
|
+
email: 'user@example.com',
|
|
70
|
+
timestamp: 1234567890,
|
|
71
|
+
sg_message_id: 'msg-123',
|
|
72
|
+
url: 'https://example.com/link'
|
|
73
|
+
},
|
|
74
|
+
headers: {}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const result = normalizer.normalize(webhook);
|
|
78
|
+
|
|
79
|
+
expect(result.type).toBe(EmailEventType.CLICKED);
|
|
80
|
+
expect(result.metadata.url).toBe('https://example.com/link');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should normalize bounce event with hard bounce', () => {
|
|
84
|
+
const webhook: WebhookPayload = {
|
|
85
|
+
provider: EmailProvider.SENDGRID,
|
|
86
|
+
signature: 'test-signature',
|
|
87
|
+
payload: {
|
|
88
|
+
event: 'bounce',
|
|
89
|
+
email: 'user@example.com',
|
|
90
|
+
timestamp: 1234567890,
|
|
91
|
+
sg_message_id: 'msg-123',
|
|
92
|
+
status: '5.1.1',
|
|
93
|
+
reason: 'User unknown'
|
|
94
|
+
},
|
|
95
|
+
headers: {}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const result = normalizer.normalize(webhook);
|
|
99
|
+
|
|
100
|
+
expect(result.type).toBe(EmailEventType.BOUNCED);
|
|
101
|
+
expect(result.metadata.bounceType).toBe('hard');
|
|
102
|
+
expect(result.metadata.bounceReason).toBe('User unknown');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should normalize bounce event with soft bounce', () => {
|
|
106
|
+
const webhook: WebhookPayload = {
|
|
107
|
+
provider: EmailProvider.SENDGRID,
|
|
108
|
+
signature: 'test-signature',
|
|
109
|
+
payload: {
|
|
110
|
+
event: 'bounce',
|
|
111
|
+
email: 'user@example.com',
|
|
112
|
+
timestamp: 1234567890,
|
|
113
|
+
sg_message_id: 'msg-123',
|
|
114
|
+
status: '4.2.2',
|
|
115
|
+
reason: 'Mailbox full'
|
|
116
|
+
},
|
|
117
|
+
headers: {}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const result = normalizer.normalize(webhook);
|
|
121
|
+
|
|
122
|
+
expect(result.type).toBe(EmailEventType.BOUNCED);
|
|
123
|
+
expect(result.metadata.bounceType).toBe('soft');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should normalize spam complaint event', () => {
|
|
127
|
+
const webhook: WebhookPayload = {
|
|
128
|
+
provider: EmailProvider.SENDGRID,
|
|
129
|
+
signature: 'test-signature',
|
|
130
|
+
payload: {
|
|
131
|
+
event: 'spamreport',
|
|
132
|
+
email: 'user@example.com',
|
|
133
|
+
timestamp: 1234567890,
|
|
134
|
+
sg_message_id: 'msg-123'
|
|
135
|
+
},
|
|
136
|
+
headers: {}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const result = normalizer.normalize(webhook);
|
|
140
|
+
|
|
141
|
+
expect(result.type).toBe(EmailEventType.COMPLAINED);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should detect tablet device type', () => {
|
|
145
|
+
const webhook: WebhookPayload = {
|
|
146
|
+
provider: EmailProvider.SENDGRID,
|
|
147
|
+
signature: 'test-signature',
|
|
148
|
+
payload: {
|
|
149
|
+
event: 'open',
|
|
150
|
+
email: 'user@example.com',
|
|
151
|
+
timestamp: 1234567890,
|
|
152
|
+
sg_message_id: 'msg-123',
|
|
153
|
+
useragent: 'Mozilla/5.0 (iPad; CPU OS 14_0 like Mac OS X)'
|
|
154
|
+
},
|
|
155
|
+
headers: {}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const result = normalizer.normalize(webhook);
|
|
159
|
+
|
|
160
|
+
expect(result.metadata.deviceType).toBe('tablet');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should detect desktop device type', () => {
|
|
164
|
+
const webhook: WebhookPayload = {
|
|
165
|
+
provider: EmailProvider.SENDGRID,
|
|
166
|
+
signature: 'test-signature',
|
|
167
|
+
payload: {
|
|
168
|
+
event: 'open',
|
|
169
|
+
email: 'user@example.com',
|
|
170
|
+
timestamp: 1234567890,
|
|
171
|
+
sg_message_id: 'msg-123',
|
|
172
|
+
useragent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
|
173
|
+
},
|
|
174
|
+
headers: {}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const result = normalizer.normalize(webhook);
|
|
178
|
+
|
|
179
|
+
expect(result.metadata.deviceType).toBe('desktop');
|
|
180
|
+
});
|
|
181
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
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
|
+
|
|
9
|
+
import { InMemoryPersistenceAdapter } from '../../src/persistence/InMemoryPersistenceAdapter';
|
|
10
|
+
import { EmailEvent, EmailEventType, EmailProvider } from '../../src/types';
|
|
11
|
+
|
|
12
|
+
describe('InMemoryPersistenceAdapter', () => {
|
|
13
|
+
let adapter: InMemoryPersistenceAdapter;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
adapter = new InMemoryPersistenceAdapter();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const createEvent = (overrides: Partial<EmailEvent> = {}): EmailEvent => ({
|
|
20
|
+
id: `event-${Math.random()}`,
|
|
21
|
+
emailId: 'email-123',
|
|
22
|
+
messageId: 'msg-123',
|
|
23
|
+
type: EmailEventType.DELIVERED,
|
|
24
|
+
recipient: 'user@example.com',
|
|
25
|
+
timestamp: new Date(),
|
|
26
|
+
provider: EmailProvider.SENDGRID,
|
|
27
|
+
metadata: {},
|
|
28
|
+
...overrides
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should save and retrieve events', async () => {
|
|
32
|
+
const event = createEvent();
|
|
33
|
+
await adapter.saveEvent(event);
|
|
34
|
+
|
|
35
|
+
const results = await adapter.queryEvents({});
|
|
36
|
+
expect(results).toHaveLength(1);
|
|
37
|
+
expect(results[0].id).toBe(event.id);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should query events by email ID', async () => {
|
|
41
|
+
await adapter.saveEvent(createEvent({ emailId: 'email-1' }));
|
|
42
|
+
await adapter.saveEvent(createEvent({ emailId: 'email-2' }));
|
|
43
|
+
|
|
44
|
+
const results = await adapter.queryEvents({ emailId: 'email-1' });
|
|
45
|
+
expect(results).toHaveLength(1);
|
|
46
|
+
expect(results[0].emailId).toBe('email-1');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should query events by recipient', async () => {
|
|
50
|
+
await adapter.saveEvent(createEvent({ recipient: 'user1@example.com' }));
|
|
51
|
+
await adapter.saveEvent(createEvent({ recipient: 'user2@example.com' }));
|
|
52
|
+
|
|
53
|
+
const results = await adapter.queryEvents({ recipient: 'user1@example.com' });
|
|
54
|
+
expect(results).toHaveLength(1);
|
|
55
|
+
expect(results[0].recipient).toBe('user1@example.com');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should query events by event types', async () => {
|
|
59
|
+
await adapter.saveEvent(createEvent({ type: EmailEventType.DELIVERED }));
|
|
60
|
+
await adapter.saveEvent(createEvent({ type: EmailEventType.OPENED }));
|
|
61
|
+
|
|
62
|
+
const results = await adapter.queryEvents({
|
|
63
|
+
eventTypes: [EmailEventType.DELIVERED]
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(results).toHaveLength(1);
|
|
67
|
+
expect(results[0].type).toBe(EmailEventType.DELIVERED);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should query events by date range', async () => {
|
|
71
|
+
await adapter.saveEvent(createEvent({ timestamp: new Date('2025-01-01') }));
|
|
72
|
+
await adapter.saveEvent(createEvent({ timestamp: new Date('2025-06-01') }));
|
|
73
|
+
await adapter.saveEvent(createEvent({ timestamp: new Date('2025-12-01') }));
|
|
74
|
+
|
|
75
|
+
const results = await adapter.queryEvents({
|
|
76
|
+
startDate: new Date('2025-05-01'),
|
|
77
|
+
endDate: new Date('2025-07-01')
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
expect(results).toHaveLength(1);
|
|
81
|
+
expect(results[0].timestamp).toEqual(new Date('2025-06-01'));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should apply limit and offset', async () => {
|
|
85
|
+
await adapter.saveEvent(createEvent());
|
|
86
|
+
await adapter.saveEvent(createEvent());
|
|
87
|
+
await adapter.saveEvent(createEvent());
|
|
88
|
+
await adapter.saveEvent(createEvent());
|
|
89
|
+
|
|
90
|
+
const results = await adapter.queryEvents({ limit: 2, offset: 1 });
|
|
91
|
+
expect(results).toHaveLength(2);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should clear all events', async () => {
|
|
95
|
+
await adapter.saveEvent(createEvent());
|
|
96
|
+
await adapter.saveEvent(createEvent());
|
|
97
|
+
|
|
98
|
+
expect(adapter.getEventCount()).toBe(2);
|
|
99
|
+
|
|
100
|
+
adapter.clearEvents();
|
|
101
|
+
expect(adapter.getEventCount()).toBe(0);
|
|
102
|
+
});
|
|
103
|
+
});
|