@stacksjs/ts-cloud-core 0.1.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.
Files changed (251) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +321 -0
  3. package/package.json +31 -0
  4. package/src/advanced-features.test.ts +465 -0
  5. package/src/aws/cloudformation.ts +421 -0
  6. package/src/aws/cloudfront.ts +158 -0
  7. package/src/aws/credentials.test.ts +132 -0
  8. package/src/aws/credentials.ts +545 -0
  9. package/src/aws/index.ts +87 -0
  10. package/src/aws/s3.test.ts +188 -0
  11. package/src/aws/s3.ts +1088 -0
  12. package/src/aws/signature.test.ts +670 -0
  13. package/src/aws/signature.ts +1155 -0
  14. package/src/backup/disaster-recovery.test.ts +726 -0
  15. package/src/backup/disaster-recovery.ts +500 -0
  16. package/src/backup/index.ts +34 -0
  17. package/src/backup/manager.test.ts +498 -0
  18. package/src/backup/manager.ts +432 -0
  19. package/src/cicd/circleci.ts +430 -0
  20. package/src/cicd/github-actions.ts +424 -0
  21. package/src/cicd/gitlab-ci.ts +255 -0
  22. package/src/cicd/index.ts +8 -0
  23. package/src/cli/history.ts +396 -0
  24. package/src/cli/index.ts +10 -0
  25. package/src/cli/progress.ts +458 -0
  26. package/src/cli/repl.ts +454 -0
  27. package/src/cli/suggestions.ts +327 -0
  28. package/src/cli/table.test.ts +319 -0
  29. package/src/cli/table.ts +332 -0
  30. package/src/cloudformation/builder.test.ts +327 -0
  31. package/src/cloudformation/builder.ts +378 -0
  32. package/src/cloudformation/builders/api-gateway.ts +449 -0
  33. package/src/cloudformation/builders/cache.ts +334 -0
  34. package/src/cloudformation/builders/cdn.ts +278 -0
  35. package/src/cloudformation/builders/compute.ts +485 -0
  36. package/src/cloudformation/builders/database.ts +392 -0
  37. package/src/cloudformation/builders/functions.ts +343 -0
  38. package/src/cloudformation/builders/messaging.ts +140 -0
  39. package/src/cloudformation/builders/monitoring.ts +300 -0
  40. package/src/cloudformation/builders/network.ts +264 -0
  41. package/src/cloudformation/builders/queue.ts +147 -0
  42. package/src/cloudformation/builders/security.ts +399 -0
  43. package/src/cloudformation/builders/storage.ts +285 -0
  44. package/src/cloudformation/index.ts +30 -0
  45. package/src/cloudformation/types.ts +173 -0
  46. package/src/compliance/aws-config.ts +543 -0
  47. package/src/compliance/cloudtrail.ts +376 -0
  48. package/src/compliance/compliance.test.ts +423 -0
  49. package/src/compliance/guardduty.ts +446 -0
  50. package/src/compliance/index.ts +66 -0
  51. package/src/compliance/security-hub.ts +456 -0
  52. package/src/containers/build-optimization.ts +416 -0
  53. package/src/containers/containers.test.ts +508 -0
  54. package/src/containers/image-scanning.ts +360 -0
  55. package/src/containers/index.ts +9 -0
  56. package/src/containers/registry.ts +293 -0
  57. package/src/containers/service-mesh.ts +520 -0
  58. package/src/database/database.test.ts +762 -0
  59. package/src/database/index.ts +9 -0
  60. package/src/database/migrations.ts +444 -0
  61. package/src/database/performance.ts +528 -0
  62. package/src/database/replicas.ts +534 -0
  63. package/src/database/users.ts +494 -0
  64. package/src/dependency-graph.ts +143 -0
  65. package/src/deployment/ab-testing.ts +582 -0
  66. package/src/deployment/blue-green.ts +452 -0
  67. package/src/deployment/canary.ts +500 -0
  68. package/src/deployment/deployment.test.ts +526 -0
  69. package/src/deployment/index.ts +61 -0
  70. package/src/deployment/progressive.ts +62 -0
  71. package/src/dns/dns.test.ts +641 -0
  72. package/src/dns/dnssec.ts +315 -0
  73. package/src/dns/index.ts +8 -0
  74. package/src/dns/resolver.ts +496 -0
  75. package/src/dns/routing.ts +593 -0
  76. package/src/email/advanced/analytics.ts +445 -0
  77. package/src/email/advanced/index.ts +11 -0
  78. package/src/email/advanced/rules.ts +465 -0
  79. package/src/email/advanced/scheduling.ts +352 -0
  80. package/src/email/advanced/search.ts +412 -0
  81. package/src/email/advanced/shared-mailboxes.ts +404 -0
  82. package/src/email/advanced/templates.ts +455 -0
  83. package/src/email/advanced/threading.ts +281 -0
  84. package/src/email/analytics.ts +467 -0
  85. package/src/email/bounce-handling.ts +425 -0
  86. package/src/email/email.test.ts +431 -0
  87. package/src/email/handlers/__tests__/inbound.test.ts +38 -0
  88. package/src/email/handlers/__tests__/outbound.test.ts +37 -0
  89. package/src/email/handlers/converter.ts +227 -0
  90. package/src/email/handlers/feedback.ts +228 -0
  91. package/src/email/handlers/inbound.ts +169 -0
  92. package/src/email/handlers/outbound.ts +178 -0
  93. package/src/email/index.ts +15 -0
  94. package/src/email/reputation.ts +303 -0
  95. package/src/email/templates.ts +352 -0
  96. package/src/errors/index.test.ts +434 -0
  97. package/src/errors/index.ts +416 -0
  98. package/src/health-checks/index.ts +40 -0
  99. package/src/index.ts +360 -0
  100. package/src/intrinsic-functions.ts +118 -0
  101. package/src/lambda/concurrency.ts +330 -0
  102. package/src/lambda/destinations.ts +345 -0
  103. package/src/lambda/dlq.ts +425 -0
  104. package/src/lambda/index.ts +11 -0
  105. package/src/lambda/lambda.test.ts +840 -0
  106. package/src/lambda/layers.ts +263 -0
  107. package/src/lambda/versions.ts +376 -0
  108. package/src/lambda/vpc.ts +399 -0
  109. package/src/local/config.ts +114 -0
  110. package/src/local/index.ts +6 -0
  111. package/src/local/mock-aws.ts +351 -0
  112. package/src/modules/ai.ts +340 -0
  113. package/src/modules/api.ts +478 -0
  114. package/src/modules/auth.ts +805 -0
  115. package/src/modules/cache.ts +417 -0
  116. package/src/modules/cdn.ts +1062 -0
  117. package/src/modules/communication.ts +1094 -0
  118. package/src/modules/compute.ts +3348 -0
  119. package/src/modules/database.ts +554 -0
  120. package/src/modules/deployment.ts +1079 -0
  121. package/src/modules/dns.ts +337 -0
  122. package/src/modules/email.ts +1538 -0
  123. package/src/modules/filesystem.ts +515 -0
  124. package/src/modules/index.ts +32 -0
  125. package/src/modules/messaging.ts +486 -0
  126. package/src/modules/monitoring.ts +2086 -0
  127. package/src/modules/network.ts +664 -0
  128. package/src/modules/parameter-store.ts +325 -0
  129. package/src/modules/permissions.ts +1081 -0
  130. package/src/modules/phone.ts +494 -0
  131. package/src/modules/queue.ts +1260 -0
  132. package/src/modules/redirects.ts +464 -0
  133. package/src/modules/registry.ts +699 -0
  134. package/src/modules/search.ts +401 -0
  135. package/src/modules/secrets.ts +416 -0
  136. package/src/modules/security.ts +731 -0
  137. package/src/modules/sms.ts +389 -0
  138. package/src/modules/storage.ts +1120 -0
  139. package/src/modules/workflow.ts +680 -0
  140. package/src/multi-account/config.ts +521 -0
  141. package/src/multi-account/index.ts +7 -0
  142. package/src/multi-account/manager.ts +427 -0
  143. package/src/multi-region/cross-region.ts +410 -0
  144. package/src/multi-region/index.ts +8 -0
  145. package/src/multi-region/manager.ts +483 -0
  146. package/src/multi-region/regions.ts +435 -0
  147. package/src/network-security/index.ts +48 -0
  148. package/src/observability/index.ts +9 -0
  149. package/src/observability/logs.ts +522 -0
  150. package/src/observability/metrics.ts +460 -0
  151. package/src/observability/observability.test.ts +782 -0
  152. package/src/observability/synthetics.ts +568 -0
  153. package/src/observability/xray.ts +358 -0
  154. package/src/phone/advanced/analytics.ts +349 -0
  155. package/src/phone/advanced/callbacks.ts +428 -0
  156. package/src/phone/advanced/index.ts +8 -0
  157. package/src/phone/advanced/ivr-builder.ts +504 -0
  158. package/src/phone/advanced/recording.ts +310 -0
  159. package/src/phone/handlers/__tests__/incoming-call.test.ts +40 -0
  160. package/src/phone/handlers/incoming-call.ts +117 -0
  161. package/src/phone/handlers/missed-call.ts +116 -0
  162. package/src/phone/handlers/voicemail.ts +179 -0
  163. package/src/phone/index.ts +9 -0
  164. package/src/presets/api-backend.ts +134 -0
  165. package/src/presets/data-pipeline.ts +204 -0
  166. package/src/presets/extend.test.ts +295 -0
  167. package/src/presets/extend.ts +297 -0
  168. package/src/presets/fullstack-app.ts +144 -0
  169. package/src/presets/index.ts +27 -0
  170. package/src/presets/jamstack.ts +135 -0
  171. package/src/presets/microservices.ts +167 -0
  172. package/src/presets/ml-api.ts +208 -0
  173. package/src/presets/nodejs-server.ts +104 -0
  174. package/src/presets/nodejs-serverless.ts +114 -0
  175. package/src/presets/realtime-app.ts +184 -0
  176. package/src/presets/static-site.ts +64 -0
  177. package/src/presets/traditional-web-app.ts +339 -0
  178. package/src/presets/wordpress.ts +138 -0
  179. package/src/preview/github.test.ts +249 -0
  180. package/src/preview/github.ts +297 -0
  181. package/src/preview/index.ts +37 -0
  182. package/src/preview/manager.test.ts +440 -0
  183. package/src/preview/manager.ts +326 -0
  184. package/src/preview/notifications.test.ts +582 -0
  185. package/src/preview/notifications.ts +341 -0
  186. package/src/queue/batch-processing.ts +402 -0
  187. package/src/queue/dlq-monitoring.ts +402 -0
  188. package/src/queue/fifo.ts +342 -0
  189. package/src/queue/index.ts +9 -0
  190. package/src/queue/management.ts +428 -0
  191. package/src/queue/queue.test.ts +429 -0
  192. package/src/resource-mgmt/index.ts +39 -0
  193. package/src/resource-naming.ts +62 -0
  194. package/src/s3/index.ts +523 -0
  195. package/src/schema/cloud-config.schema.json +554 -0
  196. package/src/schema/index.ts +68 -0
  197. package/src/security/certificate-manager.ts +492 -0
  198. package/src/security/index.ts +9 -0
  199. package/src/security/scanning.ts +545 -0
  200. package/src/security/secrets-manager.ts +476 -0
  201. package/src/security/secrets-rotation.ts +456 -0
  202. package/src/security/security.test.ts +738 -0
  203. package/src/sms/advanced/ab-testing.ts +389 -0
  204. package/src/sms/advanced/analytics.ts +336 -0
  205. package/src/sms/advanced/campaigns.ts +523 -0
  206. package/src/sms/advanced/chatbot.ts +224 -0
  207. package/src/sms/advanced/index.ts +10 -0
  208. package/src/sms/advanced/link-tracking.ts +248 -0
  209. package/src/sms/advanced/mms.ts +308 -0
  210. package/src/sms/handlers/__tests__/send.test.ts +40 -0
  211. package/src/sms/handlers/delivery-status.ts +133 -0
  212. package/src/sms/handlers/receive.ts +162 -0
  213. package/src/sms/handlers/send.ts +174 -0
  214. package/src/sms/index.ts +9 -0
  215. package/src/stack-diff.ts +389 -0
  216. package/src/static-site/index.ts +85 -0
  217. package/src/template-builder.ts +110 -0
  218. package/src/template-validator.ts +574 -0
  219. package/src/utils/cache.ts +291 -0
  220. package/src/utils/diff.ts +269 -0
  221. package/src/utils/hash.ts +227 -0
  222. package/src/utils/index.ts +8 -0
  223. package/src/utils/parallel.ts +294 -0
  224. package/src/validators/credentials.test.ts +274 -0
  225. package/src/validators/credentials.ts +233 -0
  226. package/src/validators/quotas.test.ts +434 -0
  227. package/src/validators/quotas.ts +217 -0
  228. package/test/ai.test.ts +327 -0
  229. package/test/api.test.ts +511 -0
  230. package/test/auth.test.ts +632 -0
  231. package/test/cache.test.ts +406 -0
  232. package/test/cdn.test.ts +247 -0
  233. package/test/compute.test.ts +861 -0
  234. package/test/database.test.ts +523 -0
  235. package/test/deployment.test.ts +499 -0
  236. package/test/dns.test.ts +270 -0
  237. package/test/email.test.ts +439 -0
  238. package/test/filesystem.test.ts +382 -0
  239. package/test/integration.test.ts +350 -0
  240. package/test/messaging.test.ts +514 -0
  241. package/test/monitoring.test.ts +634 -0
  242. package/test/network.test.ts +425 -0
  243. package/test/permissions.test.ts +488 -0
  244. package/test/queue.test.ts +484 -0
  245. package/test/registry.test.ts +306 -0
  246. package/test/security.test.ts +462 -0
  247. package/test/storage.test.ts +463 -0
  248. package/test/template-validator.test.ts +559 -0
  249. package/test/workflow.test.ts +592 -0
  250. package/tsconfig.json +16 -0
  251. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,431 @@
1
+ import { describe, expect, it, beforeEach } from 'bun:test'
2
+ import {
3
+ BounceComplaintHandler,
4
+ bounceComplaintHandler,
5
+ EmailAnalyticsManager,
6
+ emailAnalyticsManager,
7
+ SenderReputationManager,
8
+ senderReputationManager,
9
+ EmailTemplateManager,
10
+ emailTemplateManager,
11
+ } from '.'
12
+
13
+ describe('Bounce and Complaint Handler', () => {
14
+ let manager: BounceComplaintHandler
15
+
16
+ beforeEach(() => {
17
+ manager = new BounceComplaintHandler()
18
+ })
19
+
20
+ it('should record bounce event', () => {
21
+ const bounce = manager.recordBounce({
22
+ timestamp: new Date(),
23
+ messageId: 'msg-123',
24
+ recipient: 'bounce@example.com',
25
+ bounceType: 'Permanent',
26
+ bounceSubType: 'General',
27
+ feedbackId: 'fb-123',
28
+ })
29
+
30
+ expect(bounce.id).toContain('bounce')
31
+ expect(bounce.bounceType).toBe('Permanent')
32
+ })
33
+
34
+ it('should auto-suppress permanent bounces', () => {
35
+ manager.recordBounce({
36
+ timestamp: new Date(),
37
+ messageId: 'msg-123',
38
+ recipient: 'permanent@example.com',
39
+ bounceType: 'Permanent',
40
+ bounceSubType: 'General',
41
+ feedbackId: 'fb-123',
42
+ })
43
+
44
+ expect(manager.isSuppressed('permanent@example.com')).toBe(true)
45
+ })
46
+
47
+ it('should record complaint event', () => {
48
+ const complaint = manager.recordComplaint({
49
+ timestamp: new Date(),
50
+ messageId: 'msg-456',
51
+ recipients: ['complaint1@example.com', 'complaint2@example.com'],
52
+ feedbackId: 'fb-456',
53
+ })
54
+
55
+ expect(complaint.id).toContain('complaint')
56
+ expect(complaint.recipients).toHaveLength(2)
57
+ })
58
+
59
+ it('should create automatic bounce handler', () => {
60
+ const handler = manager.createAutomaticBounceHandler({
61
+ name: 'auto-handler',
62
+ notificationTopicArn: 'arn:aws:sns:us-east-1:123456789012:topic',
63
+ })
64
+
65
+ expect(handler.autoSuppress).toBe(true)
66
+ expect(handler.bounceThreshold).toBe(5)
67
+ })
68
+
69
+ it('should get bounce statistics', () => {
70
+ const start = new Date(Date.now() - 24 * 60 * 60 * 1000)
71
+ const end = new Date()
72
+
73
+ manager.recordBounce({
74
+ timestamp: new Date(),
75
+ messageId: 'msg-1',
76
+ recipient: 'test1@example.com',
77
+ bounceType: 'Permanent',
78
+ bounceSubType: 'General',
79
+ feedbackId: 'fb-1',
80
+ })
81
+
82
+ manager.recordBounce({
83
+ timestamp: new Date(),
84
+ messageId: 'msg-2',
85
+ recipient: 'test2@example.com',
86
+ bounceType: 'Transient',
87
+ bounceSubType: 'General',
88
+ feedbackId: 'fb-2',
89
+ })
90
+
91
+ const stats = manager.getBounceStatistics(start, end)
92
+
93
+ expect(stats.totalBounces).toBe(2)
94
+ expect(stats.permanentBounces).toBe(1)
95
+ expect(stats.transientBounces).toBe(1)
96
+ })
97
+
98
+ it('should use global instance', () => {
99
+ expect(bounceComplaintHandler).toBeInstanceOf(BounceComplaintHandler)
100
+ })
101
+ })
102
+
103
+ describe('Email Analytics Manager', () => {
104
+ let manager: EmailAnalyticsManager
105
+
106
+ beforeEach(() => {
107
+ manager = new EmailAnalyticsManager()
108
+ })
109
+
110
+ it('should track open event', () => {
111
+ const event = manager.trackOpen({
112
+ messageId: 'msg-123',
113
+ recipient: 'user@example.com',
114
+ ipAddress: '192.0.2.1',
115
+ userAgent: 'Mozilla/5.0',
116
+ })
117
+
118
+ expect(event.eventType).toBe('open')
119
+ expect(event.ipAddress).toBe('192.0.2.1')
120
+ })
121
+
122
+ it('should track click event', () => {
123
+ const event = manager.trackClick({
124
+ messageId: 'msg-123',
125
+ recipient: 'user@example.com',
126
+ link: 'https://example.com/landing',
127
+ ipAddress: '192.0.2.1',
128
+ userAgent: 'Mozilla/5.0',
129
+ })
130
+
131
+ expect(event.eventType).toBe('click')
132
+ expect(event.link).toBe('https://example.com/landing')
133
+ })
134
+
135
+ it('should create campaign', () => {
136
+ const campaign = manager.createCampaign({
137
+ name: 'Summer Sale',
138
+ subject: 'Big Summer Sale!',
139
+ fromEmail: 'sales@example.com',
140
+ tags: { category: 'promotional' },
141
+ })
142
+
143
+ expect(campaign.id).toContain('campaign')
144
+ expect(campaign.sentCount).toBe(0)
145
+ })
146
+
147
+ it('should generate analytics report', () => {
148
+ const campaign = manager.createCampaign({
149
+ name: 'Test Campaign',
150
+ subject: 'Test',
151
+ fromEmail: 'test@example.com',
152
+ tags: { campaignId: 'camp-1' },
153
+ })
154
+
155
+ // Track events
156
+ manager.trackEvent({
157
+ eventType: 'send',
158
+ messageId: 'msg-1',
159
+ recipient: 'user1@example.com',
160
+ timestamp: new Date(),
161
+ tags: { campaignId: campaign.id },
162
+ })
163
+
164
+ manager.trackEvent({
165
+ eventType: 'delivery',
166
+ messageId: 'msg-1',
167
+ recipient: 'user1@example.com',
168
+ timestamp: new Date(),
169
+ tags: { campaignId: campaign.id },
170
+ })
171
+
172
+ manager.trackOpen({
173
+ messageId: 'msg-1',
174
+ recipient: 'user1@example.com',
175
+ ipAddress: '192.0.2.1',
176
+ userAgent: 'Mozilla/5.0',
177
+ tags: { campaignId: campaign.id },
178
+ })
179
+
180
+ const report = manager.generateReport({
181
+ campaignId: campaign.id,
182
+ startDate: new Date(Date.now() - 24 * 60 * 60 * 1000),
183
+ endDate: new Date(),
184
+ })
185
+
186
+ expect(report.totalSent).toBe(1)
187
+ expect(report.totalDelivered).toBe(1)
188
+ expect(report.totalOpened).toBe(1)
189
+ })
190
+
191
+ it('should create A/B test', () => {
192
+ const abTest = manager.createABTest({
193
+ name: 'Subject Line Test',
194
+ variants: [
195
+ { name: 'A', subject: 'Subject A', content: 'Content A', weight: 50 },
196
+ { name: 'B', subject: 'Subject B', content: 'Content B', weight: 50 },
197
+ ],
198
+ })
199
+
200
+ expect(abTest.status).toBe('draft')
201
+ expect(abTest.variants).toHaveLength(2)
202
+ })
203
+
204
+ it('should start and complete A/B test', () => {
205
+ const abTest = manager.createABTest({
206
+ name: 'Test',
207
+ variants: [
208
+ { name: 'A', subject: 'A', content: 'A', weight: 50 },
209
+ { name: 'B', subject: 'B', content: 'B', weight: 50 },
210
+ ],
211
+ })
212
+
213
+ manager.startABTest(abTest.id)
214
+ expect(abTest.status).toBe('running')
215
+
216
+ manager.completeABTest(abTest.id)
217
+ expect(abTest.status).toBe('completed')
218
+ expect(abTest.winner).toBeDefined()
219
+ })
220
+
221
+ it('should use global instance', () => {
222
+ expect(emailAnalyticsManager).toBeInstanceOf(EmailAnalyticsManager)
223
+ })
224
+ })
225
+
226
+ describe('Sender Reputation Manager', () => {
227
+ let manager: SenderReputationManager
228
+
229
+ beforeEach(() => {
230
+ manager = new SenderReputationManager()
231
+ })
232
+
233
+ it('should get reputation dashboard', () => {
234
+ const dashboard = manager.getReputationDashboard()
235
+
236
+ expect(dashboard.id).toContain('dashboard')
237
+ expect(dashboard.overallScore).toBeGreaterThanOrEqual(0)
238
+ expect(dashboard.overallScore).toBeLessThanOrEqual(100)
239
+ expect(dashboard.blacklistStatus).toBeDefined()
240
+ })
241
+
242
+ it('should create warmup plan', () => {
243
+ const warmup = manager.createWarmupPlan({
244
+ name: 'New Domain Warmup',
245
+ initialDailyLimit: 100,
246
+ targetDailyLimit: 10000,
247
+ durationDays: 20,
248
+ })
249
+
250
+ expect(warmup.id).toContain('warmup')
251
+ expect(warmup.dailyLimits).toHaveLength(20)
252
+ expect(warmup.status).toBe('active')
253
+ })
254
+
255
+ it('should create aggressive warmup plan', () => {
256
+ const warmup = manager.createAggressiveWarmupPlan({
257
+ name: 'Aggressive Warmup',
258
+ })
259
+
260
+ expect(warmup.totalDays).toBe(14)
261
+ expect(warmup.dailyLimits[0]).toBe(500)
262
+ })
263
+
264
+ it('should create conservative warmup plan', () => {
265
+ const warmup = manager.createConservativeWarmupPlan({
266
+ name: 'Conservative Warmup',
267
+ })
268
+
269
+ expect(warmup.totalDays).toBe(30)
270
+ expect(warmup.dailyLimits[0]).toBe(200)
271
+ })
272
+
273
+ it('should advance warmup plan', () => {
274
+ const warmup = manager.createWarmupPlan({
275
+ name: 'Test Warmup',
276
+ initialDailyLimit: 100,
277
+ targetDailyLimit: 1000,
278
+ durationDays: 10,
279
+ })
280
+
281
+ const initialDay = warmup.currentDay
282
+ manager.advanceWarmupPlan(warmup.id)
283
+
284
+ expect(warmup.currentDay).toBe(initialDay + 1)
285
+ })
286
+
287
+ it('should track domain reputation', () => {
288
+ const reputation = manager.trackDomainReputation({
289
+ domain: 'example.com',
290
+ totalSent: 1000,
291
+ delivered: 950,
292
+ opened: 300,
293
+ clicked: 100,
294
+ bounced: 30,
295
+ complained: 2,
296
+ })
297
+
298
+ expect(reputation.domain).toBe('example.com')
299
+ expect(reputation.deliveryRate).toBeGreaterThan(0)
300
+ expect(reputation.reputationScore).toBeDefined()
301
+ })
302
+
303
+ it('should use global instance', () => {
304
+ expect(senderReputationManager).toBeInstanceOf(SenderReputationManager)
305
+ })
306
+ })
307
+
308
+ describe('Email Template Manager', () => {
309
+ let manager: EmailTemplateManager
310
+
311
+ beforeEach(() => {
312
+ manager = new EmailTemplateManager()
313
+ })
314
+
315
+ it('should create template', () => {
316
+ const template = manager.createTemplate({
317
+ name: 'Welcome Email',
318
+ subject: 'Welcome {{userName}}!',
319
+ htmlPart: '<h1>Welcome {{userName}}</h1>',
320
+ textPart: 'Welcome {{userName}}',
321
+ })
322
+
323
+ expect(template.id).toContain('template')
324
+ expect(template.variables).toContain('userName')
325
+ expect(template.version).toBe(1)
326
+ })
327
+
328
+ it('should create welcome template', () => {
329
+ const template = manager.createWelcomeTemplate({
330
+ name: 'Welcome',
331
+ companyName: 'Acme Inc',
332
+ })
333
+
334
+ expect(template.subject).toContain('Welcome')
335
+ expect(template.variables).toContain('userName')
336
+ expect(template.variables).toContain('companyName')
337
+ })
338
+
339
+ it('should create password reset template', () => {
340
+ const template = manager.createPasswordResetTemplate({
341
+ name: 'Password Reset',
342
+ companyName: 'Acme Inc',
343
+ })
344
+
345
+ expect(template.subject).toContain('password')
346
+ expect(template.variables).toContain('resetUrl')
347
+ })
348
+
349
+ it('should update template', () => {
350
+ const template = manager.createTemplate({
351
+ name: 'Test',
352
+ subject: 'Original Subject',
353
+ htmlPart: '<p>Original</p>',
354
+ textPart: 'Original',
355
+ })
356
+
357
+ manager.updateTemplate(template.id, {
358
+ subject: 'Updated Subject',
359
+ }, 'Changed subject line')
360
+
361
+ expect(template.subject).toBe('Updated Subject')
362
+ expect(template.version).toBe(2)
363
+ })
364
+
365
+ it('should render template', () => {
366
+ const template = manager.createTemplate({
367
+ name: 'Test',
368
+ subject: 'Hello {{name}}!',
369
+ htmlPart: '<p>Welcome {{name}} to {{company}}</p>',
370
+ textPart: 'Welcome {{name}} to {{company}}',
371
+ })
372
+
373
+ const rendered = manager.renderTemplate(template.id, {
374
+ name: 'John',
375
+ company: 'Acme Inc',
376
+ })
377
+
378
+ expect(rendered.subject).toBe('Hello John!')
379
+ expect(rendered.html).toContain('Welcome John to Acme Inc')
380
+ })
381
+
382
+ it('should test template', () => {
383
+ const template = manager.createTemplate({
384
+ name: 'Test',
385
+ subject: 'Test {{var}}',
386
+ htmlPart: '<p>{{var}}</p>',
387
+ textPart: '{{var}}',
388
+ })
389
+
390
+ const test = manager.testTemplate(template.id, { var: 'value' })
391
+
392
+ expect(test.renderedSubject).toBe('Test value')
393
+ expect(test.renderedHtml).toContain('value')
394
+ })
395
+
396
+ it('should get template versions', () => {
397
+ const template = manager.createTemplate({
398
+ name: 'Test',
399
+ subject: 'V1',
400
+ htmlPart: '<p>V1</p>',
401
+ textPart: 'V1',
402
+ })
403
+
404
+ manager.updateTemplate(template.id, { subject: 'V2' })
405
+ manager.updateTemplate(template.id, { subject: 'V3' })
406
+
407
+ const versions = manager.getTemplateVersions(template.id)
408
+
409
+ expect(versions).toHaveLength(3)
410
+ expect(versions[0].version).toBe(1)
411
+ expect(versions[2].version).toBe(3)
412
+ })
413
+
414
+ it('should revert to version', () => {
415
+ const template = manager.createTemplate({
416
+ name: 'Test',
417
+ subject: 'V1',
418
+ htmlPart: '<p>V1</p>',
419
+ textPart: 'V1',
420
+ })
421
+
422
+ manager.updateTemplate(template.id, { subject: 'V2' })
423
+ manager.revertToVersion(template.id, 1)
424
+
425
+ expect(template.subject).toBe('V1')
426
+ })
427
+
428
+ it('should use global instance', () => {
429
+ expect(emailTemplateManager).toBeInstanceOf(EmailTemplateManager)
430
+ })
431
+ })
@@ -0,0 +1,38 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { handler } from '../inbound'
3
+
4
+ describe('Inbound Email Handler', () => {
5
+ it('should export handler code as string', () => {
6
+ expect(typeof handler).toBe('string')
7
+ expect(handler).toContain('exports.handler')
8
+ })
9
+
10
+ it('should contain S3 client import', () => {
11
+ expect(handler).toContain('S3Client')
12
+ expect(handler).toContain('GetObjectCommand')
13
+ expect(handler).toContain('PutObjectCommand')
14
+ })
15
+
16
+ it('should handle SES notification parsing', () => {
17
+ expect(handler).toContain('sesNotification')
18
+ expect(handler).toContain('mail.messageId')
19
+ })
20
+
21
+ it('should organize emails by mailbox structure', () => {
22
+ expect(handler).toContain('mailboxes/')
23
+ expect(handler).toContain('metadata.json')
24
+ expect(handler).toContain('inbox.json')
25
+ })
26
+
27
+ it('should support + addressing', () => {
28
+ expect(handler).toContain("split('+')") // Handle user+tag@domain.com
29
+ })
30
+
31
+ it('should extract email metadata', () => {
32
+ expect(handler).toContain('from')
33
+ expect(handler).toContain('to')
34
+ expect(handler).toContain('subject')
35
+ expect(handler).toContain('spamVerdict')
36
+ expect(handler).toContain('virusVerdict')
37
+ })
38
+ })
@@ -0,0 +1,37 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { handler } from '../outbound'
3
+
4
+ describe('Outbound Email Handler', () => {
5
+ it('should export handler code as string', () => {
6
+ expect(typeof handler).toBe('string')
7
+ expect(handler).toContain('exports.handler')
8
+ })
9
+
10
+ it('should contain SES client import', () => {
11
+ expect(handler).toContain('SESClient')
12
+ expect(handler).toContain('SendRawEmailCommand')
13
+ })
14
+
15
+ it('should build MIME messages', () => {
16
+ expect(handler).toContain('MIME-Version')
17
+ expect(handler).toContain('Content-Type')
18
+ expect(handler).toContain('boundary')
19
+ })
20
+
21
+ it('should support HTML and text content', () => {
22
+ expect(handler).toContain('text/html')
23
+ expect(handler).toContain('text/plain')
24
+ expect(handler).toContain('multipart/alternative')
25
+ })
26
+
27
+ it('should handle attachments', () => {
28
+ expect(handler).toContain('attachments')
29
+ expect(handler).toContain('Content-Disposition: attachment')
30
+ expect(handler).toContain('multipart/mixed')
31
+ })
32
+
33
+ it('should store sent emails', () => {
34
+ expect(handler).toContain('sent/')
35
+ expect(handler).toContain('metadata.json')
36
+ })
37
+ })