@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,782 @@
1
+ import { describe, expect, it, beforeEach } from 'bun:test'
2
+ import {
3
+ XRayManager,
4
+ xrayManager,
5
+ MetricsManager,
6
+ metricsManager,
7
+ LogsManager,
8
+ logsManager,
9
+ SyntheticsManager,
10
+ syntheticsManager,
11
+ } from '.'
12
+
13
+ describe('X-Ray Manager', () => {
14
+ let manager: XRayManager
15
+
16
+ beforeEach(() => {
17
+ manager = new XRayManager()
18
+ })
19
+
20
+ describe('Configuration Creation', () => {
21
+ it('should create Lambda X-Ray configuration', () => {
22
+ const config = manager.createLambdaConfig({
23
+ functionName: 'my-function',
24
+ samplingRate: 0.2,
25
+ })
26
+
27
+ expect(config.id).toContain('xray-config')
28
+ expect(config.name).toBe('my-function-xray')
29
+ expect(config.serviceName).toBe('my-function')
30
+ expect(config.samplingRate).toBe(0.2)
31
+ expect(config.enableActiveTracing).toBe(true)
32
+ })
33
+
34
+ it('should use default sampling rate for Lambda', () => {
35
+ const config = manager.createLambdaConfig({
36
+ functionName: 'my-function',
37
+ })
38
+
39
+ expect(config.samplingRate).toBe(0.1)
40
+ })
41
+
42
+ it('should create ECS X-Ray configuration', () => {
43
+ const config = manager.createECSConfig({
44
+ serviceName: 'web-service',
45
+ clusterName: 'production',
46
+ samplingRate: 0.15,
47
+ })
48
+
49
+ expect(config.serviceName).toBe('production/web-service')
50
+ expect(config.samplingRate).toBe(0.15)
51
+ })
52
+
53
+ it('should create API Gateway X-Ray configuration', () => {
54
+ const config = manager.createAPIGatewayConfig({
55
+ apiName: 'my-api',
56
+ stage: 'prod',
57
+ })
58
+
59
+ expect(config.serviceName).toBe('my-api/prod')
60
+ expect(config.samplingRate).toBe(0.05)
61
+ })
62
+ })
63
+
64
+ describe('Sampling Rules', () => {
65
+ it('should create high-priority sampling rule', () => {
66
+ const rule = manager.createHighPrioritySamplingRule({
67
+ ruleName: 'critical-endpoints',
68
+ serviceName: 'api',
69
+ urlPath: '/api/critical/*',
70
+ })
71
+
72
+ expect(rule.id).toContain('sampling-rule')
73
+ expect(rule.priority).toBe(100)
74
+ expect(rule.fixedRate).toBe(1.0)
75
+ expect(rule.reservoirSize).toBe(100)
76
+ expect(rule.urlPath).toBe('/api/critical/*')
77
+ })
78
+
79
+ it('should create error sampling rule', () => {
80
+ const rule = manager.createErrorSamplingRule('my-service')
81
+
82
+ expect(rule.ruleName).toBe('my-service-errors')
83
+ expect(rule.priority).toBe(200)
84
+ expect(rule.fixedRate).toBe(1.0)
85
+ expect(rule.urlPath).toBe('/error/*')
86
+ })
87
+
88
+ it('should create default sampling rule', () => {
89
+ const rule = manager.createDefaultSamplingRule('my-service', 0.1)
90
+
91
+ expect(rule.ruleName).toBe('my-service-default')
92
+ expect(rule.priority).toBe(1000)
93
+ expect(rule.fixedRate).toBe(0.1)
94
+ })
95
+ })
96
+
97
+ describe('CloudFormation Generation', () => {
98
+ it('should generate Lambda X-Ray CloudFormation', () => {
99
+ const config = manager.createLambdaConfig({
100
+ functionName: 'my-function',
101
+ })
102
+
103
+ const cf = manager.generateLambdaXRayCF(config)
104
+
105
+ expect(cf.TracingConfig.Mode).toBe('Active')
106
+ })
107
+
108
+ it('should generate API Gateway X-Ray CloudFormation', () => {
109
+ const config = manager.createAPIGatewayConfig({
110
+ apiName: 'my-api',
111
+ stage: 'prod',
112
+ })
113
+
114
+ const cf = manager.generateAPIGatewayXRayCF(config)
115
+
116
+ expect(cf.TracingEnabled).toBe(true)
117
+ })
118
+
119
+ it('should generate ECS X-Ray sidecar CloudFormation', () => {
120
+ const cf = manager.generateECSXRaySidecarCF()
121
+
122
+ expect(cf.Name).toBe('xray-daemon')
123
+ expect(cf.Image).toContain('xray')
124
+ expect(cf.PortMappings).toHaveLength(1)
125
+ expect(cf.PortMappings[0].ContainerPort).toBe(2000)
126
+ })
127
+
128
+ it('should generate sampling rule CloudFormation', () => {
129
+ const rule = manager.createHighPrioritySamplingRule({
130
+ ruleName: 'test-rule',
131
+ serviceName: 'api',
132
+ urlPath: '/test/*',
133
+ })
134
+
135
+ const cf = manager.generateSamplingRuleCF(rule)
136
+
137
+ expect(cf.Type).toBe('AWS::XRay::SamplingRule')
138
+ expect(cf.Properties.SamplingRule.RuleName).toBe('test-rule')
139
+ expect(cf.Properties.SamplingRule.Priority).toBe(100)
140
+ })
141
+ })
142
+
143
+ describe('Data Management', () => {
144
+ it('should retrieve config by id', () => {
145
+ const config = manager.createLambdaConfig({
146
+ functionName: 'my-function',
147
+ })
148
+
149
+ const retrieved = manager.getConfig(config.id)
150
+
151
+ expect(retrieved).toEqual(config)
152
+ })
153
+
154
+ it('should list all configs', () => {
155
+ manager.createLambdaConfig({ functionName: 'func1' })
156
+ manager.createECSConfig({ serviceName: 'svc1', clusterName: 'cluster1' })
157
+
158
+ const configs = manager.listConfigs()
159
+
160
+ expect(configs).toHaveLength(2)
161
+ })
162
+
163
+ it('should clear all data', () => {
164
+ manager.createLambdaConfig({ functionName: 'func1' })
165
+ manager.createHighPrioritySamplingRule({
166
+ ruleName: 'rule1',
167
+ serviceName: 'svc1',
168
+ urlPath: '/test',
169
+ })
170
+
171
+ manager.clear()
172
+
173
+ expect(manager.listConfigs()).toHaveLength(0)
174
+ expect(manager.listSamplingRules()).toHaveLength(0)
175
+ })
176
+ })
177
+
178
+ it('should use global instance', () => {
179
+ expect(xrayManager).toBeInstanceOf(XRayManager)
180
+ })
181
+ })
182
+
183
+ describe('Metrics Manager', () => {
184
+ let manager: MetricsManager
185
+
186
+ beforeEach(() => {
187
+ manager = new MetricsManager()
188
+ })
189
+
190
+ describe('Metric Creation', () => {
191
+ it('should create business metric', () => {
192
+ const metric = manager.createBusinessMetric({
193
+ name: 'OrdersPlaced',
194
+ namespace: 'MyApp/Business',
195
+ unit: 'Count',
196
+ })
197
+
198
+ expect(metric.id).toContain('metric')
199
+ expect(metric.name).toBe('OrdersPlaced')
200
+ expect(metric.unit).toBe('Count')
201
+ expect(metric.dimensions).toHaveLength(2)
202
+ expect(metric.dimensions?.[0].name).toBe('Environment')
203
+ expect(metric.dimensions?.[1].value).toBe('Business')
204
+ })
205
+
206
+ it('should create application metric', () => {
207
+ const metric = manager.createApplicationMetric({
208
+ name: 'CacheHits',
209
+ namespace: 'MyApp/Application',
210
+ unit: 'Count',
211
+ serviceName: 'api',
212
+ })
213
+
214
+ expect(metric.dimensions?.[0].value).toBe('api')
215
+ expect(metric.dimensions?.[1].value).toBe('Application')
216
+ })
217
+
218
+ it('should create performance metric', () => {
219
+ const metric = manager.createPerformanceMetric({
220
+ name: 'DatabaseQueryTime',
221
+ namespace: 'MyApp/Performance',
222
+ operation: 'getUserById',
223
+ })
224
+
225
+ expect(metric.unit).toBe('Milliseconds')
226
+ expect(metric.dimensions?.[0].value).toBe('getUserById')
227
+ })
228
+
229
+ it('should create error metric', () => {
230
+ const metric = manager.createErrorMetric({
231
+ name: 'ValidationErrors',
232
+ namespace: 'MyApp/Errors',
233
+ errorType: 'ValidationError',
234
+ })
235
+
236
+ expect(metric.unit).toBe('Count')
237
+ expect(metric.dimensions?.[0].value).toBe('ValidationError')
238
+ })
239
+ })
240
+
241
+ describe('Alarm Creation', () => {
242
+ it('should create error rate alarm', () => {
243
+ const metric = manager.createErrorMetric({
244
+ name: 'Errors',
245
+ namespace: 'MyApp',
246
+ errorType: 'All',
247
+ })
248
+
249
+ const alarm = manager.createErrorRateAlarm({
250
+ metricId: metric.id,
251
+ name: 'HighErrorRate',
252
+ threshold: 10,
253
+ snsTopicArn: 'arn:aws:sns:us-east-1:123456789012:alerts',
254
+ })
255
+
256
+ expect(alarm.id).toContain('alarm')
257
+ expect(alarm.name).toBe('HighErrorRate')
258
+ expect(alarm.threshold).toBe(10)
259
+ expect(alarm.comparisonOperator).toBe('GreaterThanThreshold')
260
+ expect(alarm.statistic).toBe('Sum')
261
+ expect(alarm.alarmActions).toContain('arn:aws:sns:us-east-1:123456789012:alerts')
262
+ })
263
+
264
+ it('should create latency alarm', () => {
265
+ const metric = manager.createPerformanceMetric({
266
+ name: 'ResponseTime',
267
+ namespace: 'MyApp',
268
+ operation: 'apiCall',
269
+ })
270
+
271
+ const alarm = manager.createLatencyAlarm({
272
+ metricId: metric.id,
273
+ name: 'HighLatency',
274
+ thresholdMs: 1000,
275
+ })
276
+
277
+ expect(alarm.threshold).toBe(1000)
278
+ expect(alarm.statistic).toBe('Average')
279
+ })
280
+
281
+ it('should create throughput alarm', () => {
282
+ const metric = manager.createBusinessMetric({
283
+ name: 'Requests',
284
+ namespace: 'MyApp',
285
+ unit: 'Count',
286
+ })
287
+
288
+ const alarm = manager.createThroughputAlarm({
289
+ metricId: metric.id,
290
+ name: 'LowThroughput',
291
+ minimumThreshold: 100,
292
+ })
293
+
294
+ expect(alarm.comparisonOperator).toBe('LessThanThreshold')
295
+ expect(alarm.threshold).toBe(100)
296
+ })
297
+
298
+ it('should throw error for non-existent metric', () => {
299
+ expect(() => {
300
+ manager.createAlarm('non-existent', {
301
+ name: 'Test',
302
+ comparisonOperator: 'GreaterThanThreshold',
303
+ evaluationPeriods: 1,
304
+ threshold: 10,
305
+ period: 60,
306
+ statistic: 'Average',
307
+ })
308
+ }).toThrow('Metric not found')
309
+ })
310
+ })
311
+
312
+ describe('CloudFormation Generation', () => {
313
+ it('should generate alarm CloudFormation', () => {
314
+ const metric = manager.createBusinessMetric({
315
+ name: 'Orders',
316
+ namespace: 'MyApp',
317
+ unit: 'Count',
318
+ })
319
+
320
+ const alarm = manager.createErrorRateAlarm({
321
+ metricId: metric.id,
322
+ name: 'HighOrders',
323
+ threshold: 100,
324
+ })
325
+
326
+ const cf = manager.generateAlarmCF(metric, alarm)
327
+
328
+ expect(cf.Type).toBe('AWS::CloudWatch::Alarm')
329
+ expect(cf.Properties.AlarmName).toBe('HighOrders')
330
+ expect(cf.Properties.MetricName).toBe('Orders')
331
+ expect(cf.Properties.Namespace).toBe('MyApp')
332
+ expect(cf.Properties.Threshold).toBe(100)
333
+ })
334
+
335
+ it('should generate composite alarm CloudFormation', () => {
336
+ const alarm = manager.createCompositeAlarm({
337
+ name: 'CriticalIssues',
338
+ description: 'Multiple critical conditions',
339
+ alarmRule: 'ALARM(HighErrors) OR ALARM(HighLatency)',
340
+ })
341
+
342
+ const cf = manager.generateCompositeAlarmCF(alarm)
343
+
344
+ expect(cf.Type).toBe('AWS::CloudWatch::CompositeAlarm')
345
+ expect(cf.Properties.AlarmRule).toContain('ALARM(HighErrors)')
346
+ })
347
+
348
+ it('should generate dashboard widget', () => {
349
+ const metric = manager.createBusinessMetric({
350
+ name: 'Revenue',
351
+ namespace: 'MyApp/Business',
352
+ unit: 'Count',
353
+ })
354
+
355
+ const widget = manager.generateDashboardWidget(metric)
356
+
357
+ expect(widget.type).toBe('metric')
358
+ expect(widget.properties.metrics[0]).toContain('Revenue')
359
+ expect(widget.properties.title).toBe('Revenue')
360
+ })
361
+ })
362
+
363
+ it('should use global instance', () => {
364
+ expect(metricsManager).toBeInstanceOf(MetricsManager)
365
+ })
366
+ })
367
+
368
+ describe('Logs Manager', () => {
369
+ let manager: LogsManager
370
+
371
+ beforeEach(() => {
372
+ manager = new LogsManager()
373
+ })
374
+
375
+ describe('Log Group Creation', () => {
376
+ it('should create Lambda log group', () => {
377
+ const group = manager.createLambdaLogGroup('my-function', 14)
378
+
379
+ expect(group.id).toContain('log-group')
380
+ expect(group.name).toBe('/aws/lambda/my-function')
381
+ expect(group.retentionDays).toBe(14)
382
+ })
383
+
384
+ it('should use default retention for Lambda', () => {
385
+ const group = manager.createLambdaLogGroup('my-function')
386
+
387
+ expect(group.retentionDays).toBe(7)
388
+ })
389
+
390
+ it('should create ECS log group', () => {
391
+ const group = manager.createECSLogGroup({
392
+ clusterName: 'production',
393
+ serviceName: 'web',
394
+ retentionDays: 30,
395
+ })
396
+
397
+ expect(group.name).toBe('/ecs/production/web')
398
+ expect(group.retentionDays).toBe(30)
399
+ })
400
+
401
+ it('should create API Gateway log group', () => {
402
+ const group = manager.createAPIGatewayLogGroup('my-api', 'prod', 30)
403
+
404
+ expect(group.name).toBe('/aws/apigateway/my-api/prod')
405
+ expect(group.retentionDays).toBe(30)
406
+ })
407
+
408
+ it('should create application log group', () => {
409
+ const group = manager.createApplicationLogGroup({
410
+ appName: 'myapp',
411
+ environment: 'production',
412
+ retentionDays: 90,
413
+ kmsKeyId: 'arn:aws:kms:us-east-1:123456789012:key/12345',
414
+ })
415
+
416
+ expect(group.name).toBe('/application/myapp/production')
417
+ expect(group.kmsKeyId).toBe('arn:aws:kms:us-east-1:123456789012:key/12345')
418
+ })
419
+ })
420
+
421
+ describe('Metric Filters', () => {
422
+ it('should create error count filter', () => {
423
+ const group = manager.createLambdaLogGroup('my-function')
424
+ const filter = manager.createErrorCountFilter(group.id, 'MyApp')
425
+
426
+ expect(filter.id).toContain('filter')
427
+ expect(filter.name).toBe('ErrorCount')
428
+ expect(filter.filterPattern).toContain('ERROR')
429
+ expect(filter.metricTransformations[0].metricName).toBe('ErrorCount')
430
+ expect(filter.metricTransformations[0].metricNamespace).toBe('MyApp')
431
+ })
432
+
433
+ it('should create latency filter', () => {
434
+ const group = manager.createLambdaLogGroup('my-function')
435
+ const filter = manager.createLatencyFilter(group.id, 'MyApp')
436
+
437
+ expect(filter.name).toBe('Latency')
438
+ expect(filter.metricTransformations[0].metricName).toBe('ResponseTime')
439
+ expect(filter.metricTransformations[0].unit).toBe('Milliseconds')
440
+ })
441
+
442
+ it('should create custom pattern filter', () => {
443
+ const group = manager.createLambdaLogGroup('my-function')
444
+ const filter = manager.createCustomPatternFilter({
445
+ logGroupId: group.id,
446
+ name: 'CustomMetric',
447
+ pattern: '[time, request_id, event_type = CUSTOM, ...]',
448
+ metricName: 'CustomEvents',
449
+ namespace: 'MyApp',
450
+ })
451
+
452
+ expect(filter.name).toBe('CustomMetric')
453
+ expect(filter.filterPattern).toContain('CUSTOM')
454
+ })
455
+
456
+ it('should throw error for non-existent log group', () => {
457
+ expect(() => {
458
+ manager.createMetricFilter('non-existent', {
459
+ name: 'Test',
460
+ filterPattern: 'ERROR',
461
+ metricTransformations: [],
462
+ })
463
+ }).toThrow('Log group not found')
464
+ })
465
+ })
466
+
467
+ describe('Subscription Filters', () => {
468
+ it('should create Kinesis subscription', () => {
469
+ const group = manager.createLambdaLogGroup('my-function')
470
+ const subscription = manager.createKinesisSubscription({
471
+ logGroupId: group.id,
472
+ kinesisStreamArn: 'arn:aws:kinesis:us-east-1:123456789012:stream/logs',
473
+ roleArn: 'arn:aws:iam::123456789012:role/LogsToKinesis',
474
+ filterPattern: '[level = ERROR, ...]',
475
+ })
476
+
477
+ expect(subscription.id).toContain('subscription')
478
+ expect(subscription.name).toBe('KinesisSubscription')
479
+ expect(subscription.destinationArn).toContain('kinesis')
480
+ expect(subscription.filterPattern).toBe('[level = ERROR, ...]')
481
+ expect(subscription.distribution).toBe('Random')
482
+ })
483
+
484
+ it('should create Lambda subscription', () => {
485
+ const group = manager.createLambdaLogGroup('my-function')
486
+ const subscription = manager.createLambdaSubscription({
487
+ logGroupId: group.id,
488
+ lambdaFunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:process-logs',
489
+ })
490
+
491
+ expect(subscription.name).toBe('LambdaSubscription')
492
+ expect(subscription.destinationArn).toContain('lambda')
493
+ expect(subscription.distribution).toBe('ByLogStream')
494
+ })
495
+ })
496
+
497
+ describe('Log Insights Queries', () => {
498
+ it('should create error analysis query', () => {
499
+ const query = manager.createErrorAnalysisQuery(['/aws/lambda/my-function'])
500
+
501
+ expect(query.id).toContain('query')
502
+ expect(query.name).toBe('Error Analysis')
503
+ expect(query.query).toContain('ERROR')
504
+ expect(query.query).toContain('stats count()')
505
+ })
506
+
507
+ it('should create latency analysis query', () => {
508
+ const query = manager.createLatencyAnalysisQuery(['/aws/lambda/my-function'])
509
+
510
+ expect(query.name).toBe('Latency Analysis')
511
+ expect(query.query).toContain('@duration')
512
+ expect(query.query).toContain('avg(@duration)')
513
+ })
514
+
515
+ it('should create top errors query', () => {
516
+ const query = manager.createTopErrorsQuery(['/aws/lambda/my-function'])
517
+
518
+ expect(query.name).toBe('Top Errors')
519
+ expect(query.query).toContain('limit 20')
520
+ })
521
+ })
522
+
523
+ describe('CloudFormation Generation', () => {
524
+ it('should generate log group CloudFormation', () => {
525
+ const group = manager.createLambdaLogGroup('my-function', 30)
526
+ const cf = manager.generateLogGroupCF(group)
527
+
528
+ expect(cf.Type).toBe('AWS::Logs::LogGroup')
529
+ expect(cf.Properties.LogGroupName).toBe('/aws/lambda/my-function')
530
+ expect(cf.Properties.RetentionInDays).toBe(30)
531
+ })
532
+
533
+ it('should generate metric filter CloudFormation', () => {
534
+ const group = manager.createLambdaLogGroup('my-function')
535
+ const filter = manager.createErrorCountFilter(group.id, 'MyApp')
536
+ const cf = manager.generateMetricFilterCF(group, filter)
537
+
538
+ expect(cf.Type).toBe('AWS::Logs::MetricFilter')
539
+ expect(cf.Properties.FilterName).toBe('ErrorCount')
540
+ expect(cf.Properties.LogGroupName).toBe('/aws/lambda/my-function')
541
+ })
542
+
543
+ it('should generate subscription filter CloudFormation', () => {
544
+ const group = manager.createLambdaLogGroup('my-function')
545
+ const subscription = manager.createKinesisSubscription({
546
+ logGroupId: group.id,
547
+ kinesisStreamArn: 'arn:aws:kinesis:us-east-1:123456789012:stream/logs',
548
+ roleArn: 'arn:aws:iam::123456789012:role/LogsToKinesis',
549
+ })
550
+ const cf = manager.generateSubscriptionFilterCF(subscription)
551
+
552
+ expect(cf.Type).toBe('AWS::Logs::SubscriptionFilter')
553
+ expect(cf.Properties.DestinationArn).toContain('kinesis')
554
+ })
555
+
556
+ it('should generate query definition CloudFormation', () => {
557
+ const query = manager.createErrorAnalysisQuery(['/aws/lambda/my-function'])
558
+ const cf = manager.generateQueryDefinitionCF(query)
559
+
560
+ expect(cf.Type).toBe('AWS::Logs::QueryDefinition')
561
+ expect(cf.Properties.Name).toBe('Error Analysis')
562
+ })
563
+ })
564
+
565
+ it('should use global instance', () => {
566
+ expect(logsManager).toBeInstanceOf(LogsManager)
567
+ })
568
+ })
569
+
570
+ describe('Synthetics Manager', () => {
571
+ let manager: SyntheticsManager
572
+
573
+ beforeEach(() => {
574
+ manager = new SyntheticsManager()
575
+ })
576
+
577
+ describe('Runtime Versions', () => {
578
+ it('should have latest runtime versions', () => {
579
+ expect(SyntheticsManager.RuntimeVersions.NODEJS_PUPPETEER_4_0).toBe('syn-nodejs-puppeteer-4.0')
580
+ expect(SyntheticsManager.RuntimeVersions.PYTHON_SELENIUM_1_3).toBe('syn-python-selenium-1.3')
581
+ })
582
+ })
583
+
584
+ describe('Canary Creation', () => {
585
+ it('should create heartbeat canary', () => {
586
+ const canary = manager.createHeartbeatCanary({
587
+ name: 'website-heartbeat',
588
+ url: 'https://example.com',
589
+ interval: 5,
590
+ s3Bucket: 'canary-artifacts',
591
+ })
592
+
593
+ expect(canary.id).toContain('canary')
594
+ expect(canary.name).toBe('website-heartbeat')
595
+ expect(canary.description).toContain('https://example.com')
596
+ expect(canary.schedule.expression).toBe('rate(5 minutes)')
597
+ expect(canary.code.type).toBe('script')
598
+ expect(canary.code.script).toContain('heartbeat')
599
+ expect(canary.artifactS3Location).toContain('s3://canary-artifacts')
600
+ })
601
+
602
+ it('should create API monitoring canary', () => {
603
+ const canary = manager.createAPICanary({
604
+ name: 'api-monitor',
605
+ baseUrl: 'https://api.example.com',
606
+ endpoints: [
607
+ { path: '/health', method: 'GET', expectedStatus: 200 },
608
+ { path: '/users', method: 'GET', expectedStatus: 200 },
609
+ ],
610
+ interval: 10,
611
+ s3Bucket: 'canary-artifacts',
612
+ })
613
+
614
+ expect(canary.name).toBe('api-monitor')
615
+ expect(canary.code.script).toContain('/health')
616
+ expect(canary.code.script).toContain('/users')
617
+ expect(canary.runConfig?.timeoutInSeconds).toBe(120)
618
+ })
619
+
620
+ it('should create visual regression canary', () => {
621
+ const canary = manager.createVisualRegressionCanary({
622
+ name: 'homepage-visual',
623
+ url: 'https://example.com',
624
+ screenshotName: 'homepage',
625
+ interval: 60,
626
+ s3Bucket: 'canary-artifacts',
627
+ })
628
+
629
+ expect(canary.name).toBe('homepage-visual')
630
+ expect(canary.code.script).toContain('screenshot')
631
+ expect(canary.code.script).toContain('homepage.png')
632
+ expect(canary.runConfig?.memoryInMB).toBe(1024)
633
+ })
634
+
635
+ it('should create workflow canary', () => {
636
+ const canary = manager.createWorkflowCanary({
637
+ name: 'user-login-flow',
638
+ description: 'Test user login workflow',
639
+ steps: [
640
+ {
641
+ description: 'Navigate to login page',
642
+ url: 'https://example.com/login',
643
+ },
644
+ {
645
+ description: 'Submit login form',
646
+ url: 'https://example.com/login',
647
+ actions: [
648
+ { type: 'type', selector: '#username', value: 'testuser' },
649
+ { type: 'type', selector: '#password', value: 'testpass' },
650
+ { type: 'click', selector: '#submit' },
651
+ ],
652
+ },
653
+ ],
654
+ interval: 15,
655
+ s3Bucket: 'canary-artifacts',
656
+ })
657
+
658
+ expect(canary.name).toBe('user-login-flow')
659
+ expect(canary.code.script).toContain('Navigate to login page')
660
+ expect(canary.code.script).toContain('page.type')
661
+ expect(canary.code.script).toContain('page.click')
662
+ expect(canary.runConfig?.timeoutInSeconds).toBe(180)
663
+ })
664
+ })
665
+
666
+ describe('Alarm Creation', () => {
667
+ it('should create canary alarm', () => {
668
+ const canary = manager.createHeartbeatCanary({
669
+ name: 'test',
670
+ url: 'https://example.com',
671
+ interval: 5,
672
+ s3Bucket: 'bucket',
673
+ })
674
+
675
+ const alarm = manager.createAlarm(canary.id, {
676
+ name: 'CanaryFailure',
677
+ metric: 'SuccessPercent',
678
+ threshold: 90,
679
+ evaluationPeriods: 2,
680
+ snsTopicArn: 'arn:aws:sns:us-east-1:123456789012:alerts',
681
+ })
682
+
683
+ expect(alarm.id).toContain('alarm')
684
+ expect(alarm.name).toBe('CanaryFailure')
685
+ expect(alarm.metric).toBe('SuccessPercent')
686
+ expect(canary.alarms).toHaveLength(1)
687
+ })
688
+
689
+ it('should throw error for non-existent canary', () => {
690
+ expect(() => {
691
+ manager.createAlarm('non-existent', {
692
+ name: 'Test',
693
+ metric: 'SuccessPercent',
694
+ threshold: 90,
695
+ evaluationPeriods: 2,
696
+ })
697
+ }).toThrow('Canary not found')
698
+ })
699
+ })
700
+
701
+ describe('CloudFormation Generation', () => {
702
+ it('should generate canary CloudFormation with script', () => {
703
+ const canary = manager.createHeartbeatCanary({
704
+ name: 'test',
705
+ url: 'https://example.com',
706
+ interval: 5,
707
+ s3Bucket: 'bucket',
708
+ })
709
+
710
+ const cf = manager.generateCanaryCF(canary)
711
+
712
+ expect(cf.Type).toBe('AWS::Synthetics::Canary')
713
+ expect(cf.Properties.Name).toBe('test')
714
+ expect(cf.Properties.Code.Handler).toBe('index.handler')
715
+ expect(cf.Properties.Code.Script).toBeDefined()
716
+ expect(cf.Properties.Schedule.Expression).toBe('rate(5 minutes)')
717
+ expect(cf.Properties.StartCanaryAfterCreation).toBe(true)
718
+ })
719
+
720
+ it('should generate canary execution role CloudFormation', () => {
721
+ const cf = manager.generateCanaryRoleCF()
722
+
723
+ expect(cf.Type).toBe('AWS::IAM::Role')
724
+ expect(cf.Properties.ManagedPolicyArns).toContain(
725
+ 'arn:aws:iam::aws:policy/CloudWatchSyntheticsFullAccess'
726
+ )
727
+ expect(cf.Properties.Policies[0].PolicyName).toBe('CanaryS3Policy')
728
+ })
729
+ })
730
+
731
+ describe('Data Management', () => {
732
+ it('should retrieve canary by id', () => {
733
+ const canary = manager.createHeartbeatCanary({
734
+ name: 'test',
735
+ url: 'https://example.com',
736
+ interval: 5,
737
+ s3Bucket: 'bucket',
738
+ })
739
+
740
+ const retrieved = manager.getCanary(canary.id)
741
+
742
+ expect(retrieved).toEqual(canary)
743
+ })
744
+
745
+ it('should list all canaries', () => {
746
+ manager.createHeartbeatCanary({
747
+ name: 'test1',
748
+ url: 'https://example.com',
749
+ interval: 5,
750
+ s3Bucket: 'bucket',
751
+ })
752
+ manager.createAPICanary({
753
+ name: 'test2',
754
+ baseUrl: 'https://api.example.com',
755
+ endpoints: [{ path: '/health', method: 'GET', expectedStatus: 200 }],
756
+ interval: 10,
757
+ s3Bucket: 'bucket',
758
+ })
759
+
760
+ const canaries = manager.listCanaries()
761
+
762
+ expect(canaries).toHaveLength(2)
763
+ })
764
+
765
+ it('should clear all data', () => {
766
+ manager.createHeartbeatCanary({
767
+ name: 'test',
768
+ url: 'https://example.com',
769
+ interval: 5,
770
+ s3Bucket: 'bucket',
771
+ })
772
+
773
+ manager.clear()
774
+
775
+ expect(manager.listCanaries()).toHaveLength(0)
776
+ })
777
+ })
778
+
779
+ it('should use global instance', () => {
780
+ expect(syntheticsManager).toBeInstanceOf(SyntheticsManager)
781
+ })
782
+ })