@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,641 @@
1
+ import { describe, expect, it, beforeEach } from 'bun:test'
2
+ import {
3
+ Route53RoutingManager,
4
+ route53RoutingManager,
5
+ DNSSECManager,
6
+ dnssecManager,
7
+ Route53ResolverManager,
8
+ route53ResolverManager,
9
+ } from '.'
10
+
11
+ describe('Route53 Routing Manager', () => {
12
+ let manager: Route53RoutingManager
13
+
14
+ beforeEach(() => {
15
+ manager = new Route53RoutingManager()
16
+ })
17
+
18
+ describe('Weighted Routing', () => {
19
+ it('should create weighted policy', () => {
20
+ const policy = manager.createWeightedPolicy({
21
+ name: 'weighted-policy',
22
+ weight: 70,
23
+ setIdentifier: 'primary',
24
+ })
25
+
26
+ expect(policy.id).toContain('policy')
27
+ expect(policy.type).toBe('weighted')
28
+ expect(policy.weight).toBe(70)
29
+ })
30
+
31
+ it('should include health check', () => {
32
+ const policy = manager.createWeightedPolicy({
33
+ name: 'weighted-with-health',
34
+ weight: 50,
35
+ setIdentifier: 'region1',
36
+ healthCheckId: 'hc-123',
37
+ })
38
+
39
+ expect(policy.healthCheckId).toBe('hc-123')
40
+ })
41
+ })
42
+
43
+ describe('Latency Routing', () => {
44
+ it('should create latency policy', () => {
45
+ const policy = manager.createLatencyPolicy({
46
+ name: 'latency-policy',
47
+ region: 'us-east-1',
48
+ setIdentifier: 'use1',
49
+ })
50
+
51
+ expect(policy.type).toBe('latency')
52
+ expect(policy.region).toBe('us-east-1')
53
+ })
54
+ })
55
+
56
+ describe('Failover Routing', () => {
57
+ it('should create primary failover policy', () => {
58
+ const policy = manager.createFailoverPolicy({
59
+ name: 'primary-failover',
60
+ failoverType: 'PRIMARY',
61
+ setIdentifier: 'primary-endpoint',
62
+ healthCheckId: 'hc-primary',
63
+ })
64
+
65
+ expect(policy.type).toBe('failover')
66
+ expect(policy.failoverType).toBe('PRIMARY')
67
+ expect(policy.healthCheckId).toBeDefined()
68
+ })
69
+
70
+ it('should create secondary failover policy', () => {
71
+ const policy = manager.createFailoverPolicy({
72
+ name: 'secondary-failover',
73
+ failoverType: 'SECONDARY',
74
+ setIdentifier: 'secondary-endpoint',
75
+ healthCheckId: 'hc-secondary',
76
+ })
77
+
78
+ expect(policy.failoverType).toBe('SECONDARY')
79
+ })
80
+ })
81
+
82
+ describe('Geolocation Routing', () => {
83
+ it('should create geolocation policy by country', () => {
84
+ const policy = manager.createGeolocationPolicy({
85
+ name: 'geo-us',
86
+ country: 'US',
87
+ setIdentifier: 'us-endpoint',
88
+ })
89
+
90
+ expect(policy.type).toBe('geolocation')
91
+ expect(policy.country).toBe('US')
92
+ })
93
+
94
+ it('should create geolocation policy by continent', () => {
95
+ const policy = manager.createGeolocationPolicy({
96
+ name: 'geo-europe',
97
+ continent: 'EU',
98
+ setIdentifier: 'eu-endpoint',
99
+ })
100
+
101
+ expect(policy.continent).toBe('EU')
102
+ })
103
+ })
104
+
105
+ describe('Geoproximity Routing', () => {
106
+ it('should create geoproximity policy with coordinates', () => {
107
+ const policy = manager.createGeoproximityPolicy({
108
+ name: 'geo-prox',
109
+ coordinates: {
110
+ latitude: 40.7128,
111
+ longitude: -74.0060,
112
+ },
113
+ setIdentifier: 'nyc-endpoint',
114
+ })
115
+
116
+ expect(policy.type).toBe('geoproximity')
117
+ expect(policy.coordinates).toBeDefined()
118
+ })
119
+
120
+ it('should create geoproximity policy with AWS region', () => {
121
+ const policy = manager.createGeoproximityPolicy({
122
+ name: 'geo-prox-region',
123
+ awsRegion: 'us-west-2',
124
+ setIdentifier: 'usw2-endpoint',
125
+ })
126
+
127
+ expect(policy.awsRegion).toBe('us-west-2')
128
+ })
129
+
130
+ it('should support bias', () => {
131
+ const policy = manager.createGeoproximityPolicy({
132
+ name: 'geo-prox-bias',
133
+ awsRegion: 'us-east-1',
134
+ bias: 20,
135
+ setIdentifier: 'biased-endpoint',
136
+ })
137
+
138
+ expect(policy.bias).toBe(20)
139
+ })
140
+ })
141
+
142
+ describe('Health Checks', () => {
143
+ it('should create HTTP health check', async () => {
144
+ const healthCheck = manager.createHTTPHealthCheck({
145
+ name: 'http-health',
146
+ resourcePath: '/health',
147
+ fullyQualifiedDomainName: 'example.com',
148
+ port: 80,
149
+ })
150
+
151
+ expect(healthCheck.id).toContain('health-check')
152
+ expect(healthCheck.type).toBe('http')
153
+ expect(healthCheck.healthCheckStatus).toBe('Unknown')
154
+
155
+ // Wait for health check execution
156
+ await new Promise(resolve => setTimeout(resolve, 150))
157
+
158
+ expect(['Healthy', 'Unhealthy']).toContain(healthCheck.healthCheckStatus)
159
+ })
160
+
161
+ it('should create HTTPS health check', () => {
162
+ const healthCheck = manager.createHTTPHealthCheck({
163
+ name: 'https-health',
164
+ resourcePath: '/api/health',
165
+ fullyQualifiedDomainName: 'api.example.com',
166
+ port: 443,
167
+ enableSNI: true,
168
+ })
169
+
170
+ expect(healthCheck.type).toBe('https')
171
+ expect(healthCheck.enableSNI).toBe(true)
172
+ })
173
+
174
+ it('should create TCP health check', async () => {
175
+ const healthCheck = manager.createTCPHealthCheck({
176
+ name: 'tcp-health',
177
+ ipAddress: '192.0.2.1',
178
+ port: 3306,
179
+ })
180
+
181
+ expect(healthCheck.type).toBe('tcp')
182
+ expect(healthCheck.port).toBe(3306)
183
+
184
+ await new Promise(resolve => setTimeout(resolve, 150))
185
+
186
+ expect(['Healthy', 'Unhealthy']).toContain(healthCheck.healthCheckStatus)
187
+ })
188
+
189
+ it('should create calculated health check', async () => {
190
+ const hc1 = manager.createHTTPHealthCheck({
191
+ name: 'hc1',
192
+ resourcePath: '/health',
193
+ fullyQualifiedDomainName: 'server1.example.com',
194
+ })
195
+
196
+ const hc2 = manager.createHTTPHealthCheck({
197
+ name: 'hc2',
198
+ resourcePath: '/health',
199
+ fullyQualifiedDomainName: 'server2.example.com',
200
+ })
201
+
202
+ const calculated = manager.createCalculatedHealthCheck({
203
+ name: 'calculated-health',
204
+ childHealthChecks: [hc1.id, hc2.id],
205
+ healthThreshold: 1,
206
+ })
207
+
208
+ expect(calculated.type).toBe('calculated')
209
+ expect(calculated.childHealthChecks).toHaveLength(2)
210
+
211
+ await new Promise(resolve => setTimeout(resolve, 200))
212
+
213
+ expect(['Healthy', 'Unhealthy']).toContain(calculated.healthCheckStatus)
214
+ })
215
+ })
216
+
217
+ describe('Traffic Policies', () => {
218
+ it('should create failover traffic policy', () => {
219
+ const policy = manager.createFailoverTrafficPolicy({
220
+ name: 'failover-policy',
221
+ primaryEndpoint: '192.0.2.1',
222
+ secondaryEndpoint: '192.0.2.2',
223
+ })
224
+
225
+ expect(policy.id).toContain('traffic-policy')
226
+ expect(policy.version).toBe(1)
227
+ expect(policy.document.rules.failover.ruleType).toBe('failover')
228
+ })
229
+
230
+ it('should create geoproximity traffic policy', () => {
231
+ const policy = manager.createGeoproximityTrafficPolicy({
232
+ name: 'geo-policy',
233
+ locations: [
234
+ { endpoint: '192.0.2.1', region: 'us-east-1' },
235
+ { endpoint: '192.0.2.2', region: 'us-west-2' },
236
+ { endpoint: '192.0.2.3', latitude: 51.5074, longitude: -0.1278, bias: 10 },
237
+ ],
238
+ })
239
+
240
+ expect(policy.document.rules.geoproximity.ruleType).toBe('geoproximity')
241
+ expect(policy.document.rules.geoproximity.locations).toHaveLength(3)
242
+ })
243
+ })
244
+
245
+ describe('CloudFormation Generation', () => {
246
+ it('should generate health check CloudFormation', () => {
247
+ const healthCheck = manager.createHTTPHealthCheck({
248
+ name: 'test-health',
249
+ resourcePath: '/health',
250
+ fullyQualifiedDomainName: 'example.com',
251
+ })
252
+
253
+ const cf = manager.generateHealthCheckCF(healthCheck)
254
+
255
+ expect(cf.Type).toBe('AWS::Route53::HealthCheck')
256
+ expect(cf.Properties.HealthCheckConfig.Type).toBe('HTTP')
257
+ })
258
+
259
+ it('should generate weighted record set CloudFormation', () => {
260
+ const cf = manager.generateWeightedRecordSetCF({
261
+ hostedZoneId: 'Z123456',
262
+ name: 'example.com',
263
+ type: 'A',
264
+ ttl: 300,
265
+ resourceRecords: ['192.0.2.1'],
266
+ weight: 70,
267
+ setIdentifier: 'primary',
268
+ })
269
+
270
+ expect(cf.Type).toBe('AWS::Route53::RecordSet')
271
+ expect(cf.Properties.Weight).toBe(70)
272
+ })
273
+
274
+ it('should generate failover record set CloudFormation', () => {
275
+ const cf = manager.generateFailoverRecordSetCF({
276
+ hostedZoneId: 'Z123456',
277
+ name: 'example.com',
278
+ type: 'A',
279
+ ttl: 60,
280
+ resourceRecords: ['192.0.2.1'],
281
+ failover: 'PRIMARY',
282
+ setIdentifier: 'primary',
283
+ healthCheckId: 'hc-123',
284
+ })
285
+
286
+ expect(cf.Properties.Failover).toBe('PRIMARY')
287
+ expect(cf.Properties.HealthCheckId).toBe('hc-123')
288
+ })
289
+ })
290
+
291
+ it('should use global instance', () => {
292
+ expect(route53RoutingManager).toBeInstanceOf(Route53RoutingManager)
293
+ })
294
+ })
295
+
296
+ describe('DNSSEC Manager', () => {
297
+ let manager: DNSSECManager
298
+
299
+ beforeEach(() => {
300
+ manager = new DNSSECManager()
301
+ })
302
+
303
+ describe('DNSSEC Enablement', () => {
304
+ it('should enable DNSSEC', async () => {
305
+ const config = manager.enableDNSSEC({
306
+ hostedZoneId: 'Z123456',
307
+ })
308
+
309
+ expect(config.id).toContain('dnssec')
310
+ expect(config.status).toBe('SIGNING')
311
+
312
+ await new Promise(resolve => setTimeout(resolve, 150))
313
+
314
+ expect(config.status).toBe('SIGNED')
315
+ })
316
+
317
+ it('should disable DNSSEC', async () => {
318
+ const config = manager.enableDNSSEC({
319
+ hostedZoneId: 'Z123456',
320
+ })
321
+
322
+ const disabled = manager.disableDNSSEC(config.id)
323
+
324
+ expect(disabled.status).toBe('DELETING')
325
+
326
+ await new Promise(resolve => setTimeout(resolve, 150))
327
+
328
+ expect(disabled.status).toBe('NOT_SIGNING')
329
+ })
330
+ })
331
+
332
+ describe('KSK Management', () => {
333
+ it('should create KSK', () => {
334
+ const ksk = manager.createKSK({
335
+ name: 'my-ksk',
336
+ hostedZoneId: 'Z123456',
337
+ kmsKeyArn: 'arn:aws:kms:us-east-1:123456789012:key/abc123',
338
+ })
339
+
340
+ expect(ksk.id).toContain('ksk')
341
+ expect(ksk.status).toBe('ACTIVE')
342
+ expect(ksk.flag).toBe(257)
343
+ expect(ksk.dnskeyRecord).toBeDefined()
344
+ expect(ksk.dsRecord).toBeDefined()
345
+ })
346
+
347
+ it('should deactivate KSK', () => {
348
+ const ksk = manager.createKSK({
349
+ name: 'test-ksk',
350
+ hostedZoneId: 'Z123456',
351
+ kmsKeyArn: 'arn:aws:kms:us-east-1:123456789012:key/abc123',
352
+ })
353
+
354
+ const deactivated = manager.deactivateKSK(ksk.id)
355
+
356
+ expect(deactivated.status).toBe('INACTIVE')
357
+ })
358
+
359
+ it('should get DS record', () => {
360
+ const ksk = manager.createKSK({
361
+ name: 'test-ksk',
362
+ hostedZoneId: 'Z123456',
363
+ kmsKeyArn: 'arn:aws:kms:us-east-1:123456789012:key/abc123',
364
+ })
365
+
366
+ const dsRecord = manager.getDSRecord(ksk.id)
367
+
368
+ expect(dsRecord).toContain(ksk.keyTag !== undefined ? ksk.keyTag.toString() : '')
369
+ })
370
+ })
371
+
372
+ describe('DNSSEC Validation', () => {
373
+ it('should validate DNSSEC', () => {
374
+ const validation = manager.validateDNSSEC({
375
+ domain: 'example.com',
376
+ })
377
+
378
+ expect(validation.id).toContain('validation')
379
+ expect(['VALID', 'INVALID', 'INSECURE', 'BOGUS']).toContain(validation.validationStatus)
380
+ })
381
+
382
+ it('should detect insecure domain', () => {
383
+ const validation = manager.validateDNSSEC({
384
+ domain: 'insecure.example.com',
385
+ checkDNSKEY: false,
386
+ checkRRSIG: false,
387
+ })
388
+
389
+ expect(validation.validationStatus).toBe('INSECURE')
390
+ expect(validation.dnskeyPresent).toBe(false)
391
+ expect(validation.rrsigPresent).toBe(false)
392
+ })
393
+ })
394
+
395
+ describe('CloudFormation Generation', () => {
396
+ it('should generate DNSSEC CloudFormation', () => {
397
+ const config = manager.enableDNSSEC({
398
+ hostedZoneId: 'Z123456',
399
+ })
400
+
401
+ const cf = manager.generateDNSSECCF(config)
402
+
403
+ expect(cf.Type).toBe('AWS::Route53::DNSSEC')
404
+ expect(cf.Properties.HostedZoneId).toBe('Z123456')
405
+ })
406
+
407
+ it('should generate KSK CloudFormation', () => {
408
+ const ksk = manager.createKSK({
409
+ name: 'test-ksk',
410
+ hostedZoneId: 'Z123456',
411
+ kmsKeyArn: 'arn:aws:kms:us-east-1:123456789012:key/abc123',
412
+ })
413
+
414
+ const cf = manager.generateKSKCF(ksk)
415
+
416
+ expect(cf.Type).toBe('AWS::Route53::KeySigningKey')
417
+ expect(cf.Properties.Name).toBe('test-ksk')
418
+ })
419
+ })
420
+
421
+ it('should use global instance', () => {
422
+ expect(dnssecManager).toBeInstanceOf(DNSSECManager)
423
+ })
424
+ })
425
+
426
+ describe('Route53 Resolver Manager', () => {
427
+ let manager: Route53ResolverManager
428
+
429
+ beforeEach(() => {
430
+ manager = new Route53ResolverManager()
431
+ })
432
+
433
+ describe('Resolver Endpoints', () => {
434
+ it('should create inbound endpoint', async () => {
435
+ const endpoint = manager.createInboundEndpoint({
436
+ name: 'inbound-endpoint',
437
+ subnetIds: ['subnet-1', 'subnet-2'],
438
+ securityGroupIds: ['sg-123'],
439
+ })
440
+
441
+ expect(endpoint.id).toContain('endpoint')
442
+ expect(endpoint.direction).toBe('INBOUND')
443
+ expect(endpoint.status).toBe('CREATING')
444
+
445
+ await new Promise(resolve => setTimeout(resolve, 150))
446
+
447
+ expect(endpoint.status).toBe('OPERATIONAL')
448
+ expect(endpoint.ipAddresses[0].ip).toBeDefined()
449
+ })
450
+
451
+ it('should create outbound endpoint', () => {
452
+ const endpoint = manager.createOutboundEndpoint({
453
+ name: 'outbound-endpoint',
454
+ subnetIds: ['subnet-1', 'subnet-2'],
455
+ securityGroupIds: ['sg-123'],
456
+ })
457
+
458
+ expect(endpoint.direction).toBe('OUTBOUND')
459
+ })
460
+ })
461
+
462
+ describe('Resolver Rules', () => {
463
+ it('should create forward rule', async () => {
464
+ const endpoint = manager.createOutboundEndpoint({
465
+ name: 'outbound',
466
+ subnetIds: ['subnet-1'],
467
+ securityGroupIds: ['sg-123'],
468
+ })
469
+
470
+ const rule = manager.createForwardRule({
471
+ name: 'forward-rule',
472
+ domainName: 'corp.example.com',
473
+ targetIps: [{ ip: '10.0.1.5' }, { ip: '10.0.2.5' }],
474
+ resolverEndpointId: endpoint.id,
475
+ })
476
+
477
+ expect(rule.id).toContain('rule')
478
+ expect(rule.ruleType).toBe('FORWARD')
479
+ expect(rule.targetIps).toHaveLength(2)
480
+
481
+ await new Promise(resolve => setTimeout(resolve, 150))
482
+
483
+ expect(rule.status).toBe('COMPLETE')
484
+ })
485
+
486
+ it('should create system rule', () => {
487
+ const rule = manager.createSystemRule({
488
+ name: 'system-rule',
489
+ domainName: 'amazonaws.com',
490
+ })
491
+
492
+ expect(rule.ruleType).toBe('SYSTEM')
493
+ })
494
+ })
495
+
496
+ describe('DNS Firewall', () => {
497
+ it('should create firewall domain list', async () => {
498
+ const domainList = manager.createFirewallDomainList({
499
+ name: 'malware-domains',
500
+ domains: ['malware.example.com', 'phishing.example.com'],
501
+ })
502
+
503
+ expect(domainList.id).toContain('domain-list')
504
+ expect(domainList.domains).toHaveLength(2)
505
+
506
+ await new Promise(resolve => setTimeout(resolve, 150))
507
+
508
+ expect(domainList.status).toBe('COMPLETE')
509
+ })
510
+
511
+ it('should create block rule', () => {
512
+ const domainList = manager.createFirewallDomainList({
513
+ name: 'blocked-domains',
514
+ domains: ['blocked.example.com'],
515
+ })
516
+
517
+ const ruleGroup = manager.createBlockRule({
518
+ name: 'block-malware',
519
+ priority: 100,
520
+ domainListId: domainList.id,
521
+ blockResponse: 'NXDOMAIN',
522
+ })
523
+
524
+ expect(ruleGroup.rules).toHaveLength(1)
525
+ expect(ruleGroup.rules[0].action).toBe('BLOCK')
526
+ expect(ruleGroup.rules[0].blockResponse).toBe('NXDOMAIN')
527
+ })
528
+
529
+ it('should create allow rule', () => {
530
+ const domainList = manager.createFirewallDomainList({
531
+ name: 'allowed-domains',
532
+ domains: ['trusted.example.com'],
533
+ })
534
+
535
+ const ruleGroup = manager.createAllowRule({
536
+ name: 'allow-trusted',
537
+ priority: 50,
538
+ domainListId: domainList.id,
539
+ })
540
+
541
+ expect(ruleGroup.rules[0].action).toBe('ALLOW')
542
+ })
543
+
544
+ it('should create DNS firewall', () => {
545
+ const domainList = manager.createFirewallDomainList({
546
+ name: 'blocked',
547
+ domains: ['bad.example.com'],
548
+ })
549
+
550
+ const ruleGroup = manager.createBlockRule({
551
+ name: 'block-bad',
552
+ priority: 100,
553
+ domainListId: domainList.id,
554
+ })
555
+
556
+ const firewall = manager.createDNSFirewall({
557
+ name: 'my-firewall',
558
+ vpcId: 'vpc-123',
559
+ ruleGroupAssociations: [
560
+ {
561
+ firewallRuleGroupId: ruleGroup.id,
562
+ priority: 100,
563
+ mutationProtection: 'ENABLED',
564
+ },
565
+ ],
566
+ })
567
+
568
+ expect(firewall.id).toContain('firewall')
569
+ expect(firewall.firewallRuleGroupAssociations).toHaveLength(1)
570
+ })
571
+
572
+ it('should create malware protection firewall', () => {
573
+ const firewall = manager.createMalwareProtectionFirewall({
574
+ name: 'malware-protection',
575
+ vpcId: 'vpc-123',
576
+ maliciousDomains: ['malware1.com', 'malware2.com', 'phishing.com'],
577
+ })
578
+
579
+ expect(firewall.firewallRuleGroupAssociations).toHaveLength(1)
580
+ expect(firewall.firewallRuleGroupAssociations[0].mutationProtection).toBe('ENABLED')
581
+ })
582
+ })
583
+
584
+ describe('CloudFormation Generation', () => {
585
+ it('should generate resolver endpoint CloudFormation', () => {
586
+ const endpoint = manager.createInboundEndpoint({
587
+ name: 'test-endpoint',
588
+ subnetIds: ['subnet-1', 'subnet-2'],
589
+ securityGroupIds: ['sg-123'],
590
+ })
591
+
592
+ const cf = manager.generateResolverEndpointCF(endpoint)
593
+
594
+ expect(cf.Type).toBe('AWS::Route53Resolver::ResolverEndpoint')
595
+ expect(cf.Properties.Direction).toBe('INBOUND')
596
+ expect(cf.Properties.IpAddresses).toHaveLength(2)
597
+ })
598
+
599
+ it('should generate resolver rule CloudFormation', () => {
600
+ const endpoint = manager.createOutboundEndpoint({
601
+ name: 'outbound',
602
+ subnetIds: ['subnet-1'],
603
+ securityGroupIds: ['sg-123'],
604
+ })
605
+
606
+ const rule = manager.createForwardRule({
607
+ name: 'forward',
608
+ domainName: 'corp.example.com',
609
+ targetIps: [{ ip: '10.0.1.5' }],
610
+ resolverEndpointId: endpoint.id,
611
+ })
612
+
613
+ const cf = manager.generateResolverRuleCF(rule)
614
+
615
+ expect(cf.Type).toBe('AWS::Route53Resolver::ResolverRule')
616
+ expect(cf.Properties.RuleType).toBe('FORWARD')
617
+ })
618
+
619
+ it('should generate firewall rule group CloudFormation', () => {
620
+ const domainList = manager.createFirewallDomainList({
621
+ name: 'test-domains',
622
+ domains: ['test.com'],
623
+ })
624
+
625
+ const ruleGroup = manager.createBlockRule({
626
+ name: 'test-block',
627
+ priority: 100,
628
+ domainListId: domainList.id,
629
+ })
630
+
631
+ const cf = manager.generateFirewallRuleGroupCF(ruleGroup)
632
+
633
+ expect(cf.Type).toBe('AWS::Route53Resolver::FirewallRuleGroup')
634
+ expect(cf.Properties.FirewallRules).toHaveLength(1)
635
+ })
636
+ })
637
+
638
+ it('should use global instance', () => {
639
+ expect(route53ResolverManager).toBeInstanceOf(Route53ResolverManager)
640
+ })
641
+ })