@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,634 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import { Monitoring } from '../src/modules/monitoring'
3
+ import { TemplateBuilder } from '../src/template-builder'
4
+ import type { EnvironmentType } from '@stacksjs/ts-cloud-types'
5
+
6
+ const slug = 'test-app'
7
+ const environment: EnvironmentType = 'development'
8
+
9
+ describe('monitoring Module - Alarms', () => {
10
+ test('should create a basic CloudWatch alarm', () => {
11
+ const { alarm, logicalId } = Monitoring.createAlarm({
12
+ slug,
13
+ environment,
14
+ metricName: 'CPUUtilization',
15
+ namespace: 'AWS/EC2',
16
+ threshold: 80,
17
+ comparisonOperator: 'GreaterThanThreshold',
18
+ })
19
+
20
+ expect(alarm.Type).toBe('AWS::CloudWatch::Alarm')
21
+ expect(alarm.Properties?.AlarmName).toContain('test-app')
22
+ expect(alarm.Properties?.MetricName).toBe('CPUUtilization')
23
+ expect(alarm.Properties?.Namespace).toBe('AWS/EC2')
24
+ expect(alarm.Properties?.Threshold).toBe(80)
25
+ expect(alarm.Properties?.ComparisonOperator).toBe('GreaterThanThreshold')
26
+ expect(alarm.Properties?.Statistic).toBe('Average')
27
+ expect(alarm.Properties?.Period).toBe(300)
28
+ expect(alarm.Properties?.EvaluationPeriods).toBe(1)
29
+ expect(logicalId).toBeTruthy()
30
+ })
31
+
32
+ test('should create an alarm with custom configuration', () => {
33
+ const { alarm } = Monitoring.createAlarm({
34
+ slug,
35
+ environment,
36
+ alarmName: 'custom-alarm',
37
+ metricName: 'RequestCount',
38
+ namespace: 'AWS/ApplicationELB',
39
+ statistic: 'Sum',
40
+ period: 60,
41
+ evaluationPeriods: 3,
42
+ threshold: 1000,
43
+ comparisonOperator: 'GreaterThanOrEqualToThreshold',
44
+ treatMissingData: 'breaching',
45
+ datapointsToAlarm: 2,
46
+ })
47
+
48
+ expect(alarm.Properties?.AlarmName).toBe('custom-alarm')
49
+ expect(alarm.Properties?.Statistic).toBe('Sum')
50
+ expect(alarm.Properties?.Period).toBe(60)
51
+ expect(alarm.Properties?.EvaluationPeriods).toBe(3)
52
+ expect(alarm.Properties?.TreatMissingData).toBe('breaching')
53
+ expect(alarm.Properties?.DatapointsToAlarm).toBe(2)
54
+ })
55
+
56
+ test('should create an alarm with SNS actions', () => {
57
+ const snsTopicArn = 'arn:aws:sns:us-east-1:123456789012:my-topic'
58
+
59
+ const { alarm } = Monitoring.createAlarm({
60
+ slug,
61
+ environment,
62
+ metricName: 'Errors',
63
+ namespace: 'AWS/Lambda',
64
+ threshold: 5,
65
+ comparisonOperator: 'GreaterThanThreshold',
66
+ alarmActions: [snsTopicArn],
67
+ okActions: [snsTopicArn],
68
+ })
69
+
70
+ expect(alarm.Properties?.AlarmActions).toEqual([snsTopicArn])
71
+ expect(alarm.Properties?.OKActions).toEqual([snsTopicArn])
72
+ })
73
+
74
+ test('should create an alarm with dimensions', () => {
75
+ const { alarm } = Monitoring.createAlarm({
76
+ slug,
77
+ environment,
78
+ metricName: 'CPUUtilization',
79
+ namespace: 'AWS/EC2',
80
+ threshold: 80,
81
+ comparisonOperator: 'GreaterThanThreshold',
82
+ dimensions: {
83
+ InstanceId: 'i-1234567890abcdef0',
84
+ AutoScalingGroupName: 'my-asg',
85
+ },
86
+ })
87
+
88
+ expect(alarm.Properties?.Dimensions).toHaveLength(2)
89
+ expect(alarm.Properties?.Dimensions?.[0]).toEqual({
90
+ Name: 'InstanceId',
91
+ Value: 'i-1234567890abcdef0',
92
+ })
93
+ expect(alarm.Properties?.Dimensions?.[1]).toEqual({
94
+ Name: 'AutoScalingGroupName',
95
+ Value: 'my-asg',
96
+ })
97
+ })
98
+
99
+ test('should create a composite alarm', () => {
100
+ const { alarm, logicalId } = Monitoring.createCompositeAlarm({
101
+ slug,
102
+ environment,
103
+ alarmRule: 'ALARM(Alarm1) OR ALARM(Alarm2)',
104
+ alarmActions: ['arn:aws:sns:us-east-1:123456789012:my-topic'],
105
+ })
106
+
107
+ expect(alarm.Type).toBe('AWS::CloudWatch::CompositeAlarm')
108
+ expect(alarm.Properties?.AlarmName).toContain('test-app')
109
+ expect(alarm.Properties?.AlarmRule).toBe('ALARM(Alarm1) OR ALARM(Alarm2)')
110
+ expect(alarm.Properties?.AlarmActions).toHaveLength(1)
111
+ expect(logicalId).toBeTruthy()
112
+ })
113
+ })
114
+
115
+ describe('monitoring Module - Common Alarm Types', () => {
116
+ test('should create high CPU alarm', () => {
117
+ const alarmConfig = Monitoring.AlarmTypes.highCpu(
118
+ slug,
119
+ environment,
120
+ 'i-1234567890abcdef0',
121
+ 90,
122
+ )
123
+
124
+ expect(alarmConfig.metricName).toBe('CPUUtilization')
125
+ expect(alarmConfig.namespace).toBe('AWS/EC2')
126
+ expect(alarmConfig.threshold).toBe(90)
127
+ expect(alarmConfig.dimensions?.InstanceId).toBe('i-1234567890abcdef0')
128
+ })
129
+
130
+ test('should create Lambda errors alarm', () => {
131
+ const alarmConfig = Monitoring.AlarmTypes.lambdaErrors(
132
+ slug,
133
+ environment,
134
+ 'my-function',
135
+ 10,
136
+ 'arn:aws:sns:us-east-1:123456789012:alerts',
137
+ )
138
+
139
+ expect(alarmConfig.metricName).toBe('Errors')
140
+ expect(alarmConfig.namespace).toBe('AWS/Lambda')
141
+ expect(alarmConfig.threshold).toBe(10)
142
+ expect(alarmConfig.dimensions?.FunctionName).toBe('my-function')
143
+ expect(alarmConfig.alarmActions).toContain('arn:aws:sns:us-east-1:123456789012:alerts')
144
+ })
145
+
146
+ test('should create API Gateway 5xx errors alarm', () => {
147
+ const alarmConfig = Monitoring.AlarmTypes.apiGateway5xxErrors(
148
+ slug,
149
+ environment,
150
+ 'my-api',
151
+ 5,
152
+ )
153
+
154
+ expect(alarmConfig.metricName).toBe('5XXError')
155
+ expect(alarmConfig.namespace).toBe('AWS/ApiGateway')
156
+ expect(alarmConfig.threshold).toBe(5)
157
+ })
158
+
159
+ test('should create DynamoDB throttles alarm', () => {
160
+ const alarmConfig = Monitoring.AlarmTypes.dynamoDBThrottles(
161
+ slug,
162
+ environment,
163
+ 'my-table',
164
+ 3,
165
+ )
166
+
167
+ expect(alarmConfig.metricName).toBe('UserErrors')
168
+ expect(alarmConfig.namespace).toBe('AWS/DynamoDB')
169
+ expect(alarmConfig.threshold).toBe(3)
170
+ expect(alarmConfig.dimensions?.TableName).toBe('my-table')
171
+ })
172
+
173
+ test('should create RDS CPU alarm', () => {
174
+ const alarmConfig = Monitoring.AlarmTypes.rdsCpu(
175
+ slug,
176
+ environment,
177
+ 'my-db-instance',
178
+ 75,
179
+ )
180
+
181
+ expect(alarmConfig.metricName).toBe('CPUUtilization')
182
+ expect(alarmConfig.namespace).toBe('AWS/RDS')
183
+ expect(alarmConfig.threshold).toBe(75)
184
+ expect(alarmConfig.dimensions?.DBInstanceIdentifier).toBe('my-db-instance')
185
+ })
186
+
187
+ test('should create SQS queue depth alarm', () => {
188
+ const alarmConfig = Monitoring.AlarmTypes.sqsQueueDepth(
189
+ slug,
190
+ environment,
191
+ 'my-queue',
192
+ 200,
193
+ )
194
+
195
+ expect(alarmConfig.metricName).toBe('ApproximateNumberOfMessagesVisible')
196
+ expect(alarmConfig.namespace).toBe('AWS/SQS')
197
+ expect(alarmConfig.threshold).toBe(200)
198
+ expect(alarmConfig.dimensions?.QueueName).toBe('my-queue')
199
+ })
200
+ })
201
+
202
+ describe('monitoring Module - Dashboards', () => {
203
+ test('should create a CloudWatch dashboard', () => {
204
+ const widgets = [
205
+ Monitoring.DashboardWidgets.metric(
206
+ 0,
207
+ 0,
208
+ 12,
209
+ 6,
210
+ [
211
+ ['AWS/EC2', 'CPUUtilization', { InstanceId: 'i-1234567890abcdef0' }],
212
+ ],
213
+ 'CPU Utilization',
214
+ ),
215
+ Monitoring.DashboardWidgets.text(
216
+ 12,
217
+ 0,
218
+ 12,
219
+ 6,
220
+ '# Dashboard Title\nThis is a test dashboard',
221
+ ),
222
+ ]
223
+
224
+ const { dashboard, logicalId } = Monitoring.createDashboard({
225
+ slug,
226
+ environment,
227
+ widgets,
228
+ })
229
+
230
+ expect(dashboard.Type).toBe('AWS::CloudWatch::Dashboard')
231
+ expect(dashboard.Properties?.DashboardName).toContain('test-app')
232
+ expect(dashboard.Properties?.DashboardBody).toBeTruthy()
233
+ expect(logicalId).toBeTruthy()
234
+
235
+ const dashboardBody = JSON.parse(dashboard.Properties!.DashboardBody)
236
+ expect(dashboardBody.widgets).toHaveLength(2)
237
+ expect(dashboardBody.widgets[0].type).toBe('metric')
238
+ expect(dashboardBody.widgets[1].type).toBe('text')
239
+ })
240
+
241
+ test('should create metric widget', () => {
242
+ const widget = Monitoring.DashboardWidgets.metric(
243
+ 0,
244
+ 0,
245
+ 12,
246
+ 6,
247
+ [
248
+ ['AWS/Lambda', 'Invocations', { FunctionName: 'my-function' }],
249
+ ['AWS/Lambda', 'Errors', { FunctionName: 'my-function' }],
250
+ ],
251
+ 'Lambda Metrics',
252
+ )
253
+
254
+ expect(widget.type).toBe('metric')
255
+ expect(widget.x).toBe(0)
256
+ expect(widget.y).toBe(0)
257
+ expect(widget.width).toBe(12)
258
+ expect(widget.height).toBe(6)
259
+ expect(widget.properties.title).toBe('Lambda Metrics')
260
+ expect(widget.properties.metrics).toHaveLength(2)
261
+ })
262
+
263
+ test('should create text widget', () => {
264
+ const widget = Monitoring.DashboardWidgets.text(
265
+ 0,
266
+ 0,
267
+ 24,
268
+ 2,
269
+ '# Production Dashboard',
270
+ )
271
+
272
+ expect(widget.type).toBe('text')
273
+ expect(widget.properties.markdown).toBe('# Production Dashboard')
274
+ })
275
+
276
+ test('should create log widget', () => {
277
+ const widget = Monitoring.DashboardWidgets.log(
278
+ 0,
279
+ 0,
280
+ 24,
281
+ 6,
282
+ ['/aws/lambda/my-function', '/aws/lambda/another-function'],
283
+ 'Recent Logs',
284
+ )
285
+
286
+ expect(widget.type).toBe('log')
287
+ expect(widget.properties.title).toBe('Recent Logs')
288
+ expect(widget.properties.query).toContain('/aws/lambda/my-function')
289
+ })
290
+ })
291
+
292
+ describe('monitoring Module - Log Groups', () => {
293
+ test('should create a CloudWatch log group', () => {
294
+ const { logGroup, logicalId } = Monitoring.createLogGroup({
295
+ slug,
296
+ environment,
297
+ })
298
+
299
+ expect(logGroup.Type).toBe('AWS::Logs::LogGroup')
300
+ expect(logGroup.Properties?.LogGroupName).toContain('test-app')
301
+ expect(logicalId).toBeTruthy()
302
+ })
303
+
304
+ test('should create a log group with retention', () => {
305
+ const { logGroup } = Monitoring.createLogGroup({
306
+ slug,
307
+ environment,
308
+ retentionInDays: Monitoring.RetentionPeriods.ONE_WEEK,
309
+ })
310
+
311
+ expect(logGroup.Properties?.RetentionInDays).toBe(7)
312
+ })
313
+
314
+ test('should create a log group with KMS encryption', () => {
315
+ const kmsKeyId = 'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012'
316
+
317
+ const { logGroup } = Monitoring.createLogGroup({
318
+ slug,
319
+ environment,
320
+ kmsKeyId,
321
+ })
322
+
323
+ expect(logGroup.Properties?.KmsKeyId).toBe(kmsKeyId)
324
+ })
325
+
326
+ test('should create a log group with custom name', () => {
327
+ const { logGroup } = Monitoring.createLogGroup({
328
+ slug,
329
+ environment,
330
+ logGroupName: '/aws/lambda/custom-function',
331
+ retentionInDays: Monitoring.RetentionPeriods.ONE_MONTH,
332
+ })
333
+
334
+ expect(logGroup.Properties?.LogGroupName).toBe('/aws/lambda/custom-function')
335
+ expect(logGroup.Properties?.RetentionInDays).toBe(30)
336
+ })
337
+ })
338
+
339
+ describe('monitoring Module - Log Streams', () => {
340
+ test('should create a CloudWatch log stream', () => {
341
+ const { logGroup, logicalId: logGroupId } = Monitoring.createLogGroup({
342
+ slug,
343
+ environment,
344
+ })
345
+
346
+ const { logStream, logicalId } = Monitoring.createLogStream(logGroupId, {
347
+ slug,
348
+ environment,
349
+ })
350
+
351
+ expect(logStream.Type).toBe('AWS::Logs::LogStream')
352
+ expect(logStream.Properties?.LogStreamName).toContain('test-app')
353
+ expect(logicalId).toBeTruthy()
354
+ })
355
+
356
+ test('should create a log stream with custom name', () => {
357
+ const { logicalId: logGroupId } = Monitoring.createLogGroup({
358
+ slug,
359
+ environment,
360
+ })
361
+
362
+ const { logStream } = Monitoring.createLogStream(logGroupId, {
363
+ slug,
364
+ environment,
365
+ logStreamName: 'custom-stream',
366
+ })
367
+
368
+ expect(logStream.Properties?.LogStreamName).toBe('custom-stream')
369
+ })
370
+ })
371
+
372
+ describe('monitoring Module - Metric Filters', () => {
373
+ test('should create a metric filter', () => {
374
+ const { logicalId: logGroupId } = Monitoring.createLogGroup({
375
+ slug,
376
+ environment,
377
+ })
378
+
379
+ const { metricFilter, logicalId } = Monitoring.createMetricFilter(logGroupId, {
380
+ slug,
381
+ environment,
382
+ filterPattern: Monitoring.FilterPatterns.errors,
383
+ metricTransformations: [
384
+ {
385
+ metricName: 'ErrorCount',
386
+ metricNamespace: 'MyApp',
387
+ metricValue: '1',
388
+ },
389
+ ],
390
+ })
391
+
392
+ expect(metricFilter.Type).toBe('AWS::Logs::MetricFilter')
393
+ expect(metricFilter.Properties?.FilterPattern).toBe(Monitoring.FilterPatterns.errors)
394
+ expect(metricFilter.Properties?.MetricTransformations).toHaveLength(1)
395
+ expect(metricFilter.Properties?.MetricTransformations?.[0].MetricName).toBe('ErrorCount')
396
+ expect(logicalId).toBeTruthy()
397
+ })
398
+
399
+ test('should create a metric filter with custom pattern', () => {
400
+ const { logicalId: logGroupId } = Monitoring.createLogGroup({
401
+ slug,
402
+ environment,
403
+ })
404
+
405
+ const { metricFilter } = Monitoring.createMetricFilter(logGroupId, {
406
+ slug,
407
+ environment,
408
+ filterPattern: Monitoring.FilterPatterns.http5xx,
409
+ metricTransformations: [
410
+ {
411
+ metricName: '5xxErrors',
412
+ metricNamespace: 'API',
413
+ metricValue: '1',
414
+ defaultValue: 0,
415
+ },
416
+ ],
417
+ })
418
+
419
+ expect(metricFilter.Properties?.FilterPattern).toBe(Monitoring.FilterPatterns.http5xx)
420
+ expect(metricFilter.Properties?.MetricTransformations?.[0].DefaultValue).toBe(0)
421
+ })
422
+
423
+ test('should create a metric filter with multiple transformations', () => {
424
+ const { logicalId: logGroupId } = Monitoring.createLogGroup({
425
+ slug,
426
+ environment,
427
+ })
428
+
429
+ const { metricFilter } = Monitoring.createMetricFilter(logGroupId, {
430
+ slug,
431
+ environment,
432
+ filterPattern: Monitoring.FilterPatterns.all,
433
+ metricTransformations: [
434
+ {
435
+ metricName: 'TotalRequests',
436
+ metricNamespace: 'API',
437
+ metricValue: '1',
438
+ },
439
+ {
440
+ metricName: 'ResponseTime',
441
+ metricNamespace: 'API',
442
+ metricValue: '$.duration',
443
+ unit: 'Milliseconds',
444
+ },
445
+ ],
446
+ })
447
+
448
+ expect(metricFilter.Properties?.MetricTransformations).toHaveLength(2)
449
+ })
450
+ })
451
+
452
+ describe('monitoring Module - Filter Patterns', () => {
453
+ test('should have predefined filter patterns', () => {
454
+ expect(Monitoring.FilterPatterns.errors).toBe('[time, request_id, event_type = ERROR*, ...]')
455
+ expect(Monitoring.FilterPatterns.all).toBe('')
456
+ expect(Monitoring.FilterPatterns.http4xx).toBe('[..., status_code = 4*, ...]')
457
+ expect(Monitoring.FilterPatterns.http5xx).toBe('[..., status_code = 5*, ...]')
458
+ })
459
+
460
+ test('should create JSON field filter pattern', () => {
461
+ const pattern = Monitoring.FilterPatterns.jsonField('level', 'ERROR')
462
+ expect(pattern).toBe('{ $.level = "ERROR" }')
463
+ })
464
+
465
+ test('should create HTTP status filter pattern', () => {
466
+ const pattern = Monitoring.FilterPatterns.httpStatus(404)
467
+ expect(pattern).toBe('[..., status_code = 404, ...]')
468
+ })
469
+ })
470
+
471
+ describe('monitoring Module - Retention Periods', () => {
472
+ test('should have common retention periods', () => {
473
+ expect(Monitoring.RetentionPeriods.ONE_DAY).toBe(1)
474
+ expect(Monitoring.RetentionPeriods.ONE_WEEK).toBe(7)
475
+ expect(Monitoring.RetentionPeriods.ONE_MONTH).toBe(30)
476
+ expect(Monitoring.RetentionPeriods.ONE_YEAR).toBe(365)
477
+ expect(Monitoring.RetentionPeriods.NEVER_EXPIRE).toBeUndefined()
478
+ })
479
+ })
480
+
481
+ describe('monitoring Module - Integration with TemplateBuilder', () => {
482
+ test('should add alarm to template', () => {
483
+ const builder = new TemplateBuilder()
484
+
485
+ const { alarm, logicalId } = Monitoring.createAlarm({
486
+ slug,
487
+ environment,
488
+ metricName: 'CPUUtilization',
489
+ namespace: 'AWS/EC2',
490
+ threshold: 80,
491
+ comparisonOperator: 'GreaterThanThreshold',
492
+ })
493
+
494
+ builder.addResource(logicalId, alarm)
495
+
496
+ const template = builder.build()
497
+
498
+ expect(template.Resources[logicalId]).toBeDefined()
499
+ expect(template.Resources[logicalId].Type).toBe('AWS::CloudWatch::Alarm')
500
+ })
501
+
502
+ test('should add dashboard to template', () => {
503
+ const builder = new TemplateBuilder()
504
+
505
+ const { dashboard, logicalId } = Monitoring.createDashboard({
506
+ slug,
507
+ environment,
508
+ widgets: [
509
+ Monitoring.DashboardWidgets.metric(
510
+ 0,
511
+ 0,
512
+ 12,
513
+ 6,
514
+ [['AWS/EC2', 'CPUUtilization']],
515
+ ),
516
+ ],
517
+ })
518
+
519
+ builder.addResource(logicalId, dashboard)
520
+
521
+ const template = builder.build()
522
+
523
+ expect(template.Resources[logicalId]).toBeDefined()
524
+ expect(template.Resources[logicalId].Type).toBe('AWS::CloudWatch::Dashboard')
525
+ })
526
+
527
+ test('should add log group and metric filter to template', () => {
528
+ const builder = new TemplateBuilder()
529
+
530
+ const { logGroup, logicalId: logGroupId } = Monitoring.createLogGroup({
531
+ slug,
532
+ environment,
533
+ retentionInDays: Monitoring.RetentionPeriods.ONE_WEEK,
534
+ })
535
+
536
+ const { metricFilter, logicalId: filterId } = Monitoring.createMetricFilter(logGroupId, {
537
+ slug,
538
+ environment,
539
+ filterPattern: Monitoring.FilterPatterns.errors,
540
+ metricTransformations: [
541
+ {
542
+ metricName: 'ErrorCount',
543
+ metricNamespace: 'MyApp',
544
+ metricValue: '1',
545
+ },
546
+ ],
547
+ })
548
+
549
+ builder.addResource(logGroupId, logGroup)
550
+ builder.addResource(filterId, metricFilter)
551
+
552
+ const template = builder.build()
553
+
554
+ expect(template.Resources[logGroupId]).toBeDefined()
555
+ expect(template.Resources[logGroupId].Type).toBe('AWS::Logs::LogGroup')
556
+ expect(template.Resources[filterId]).toBeDefined()
557
+ expect(template.Resources[filterId].Type).toBe('AWS::Logs::MetricFilter')
558
+ })
559
+
560
+ test('should create complete monitoring stack', () => {
561
+ const builder = new TemplateBuilder()
562
+
563
+ // Create log group
564
+ const { logGroup, logicalId: logGroupId } = Monitoring.createLogGroup({
565
+ slug,
566
+ environment,
567
+ logGroupName: '/aws/lambda/my-function',
568
+ retentionInDays: Monitoring.RetentionPeriods.ONE_MONTH,
569
+ })
570
+
571
+ // Create metric filter
572
+ const { metricFilter, logicalId: filterId } = Monitoring.createMetricFilter(logGroupId, {
573
+ slug,
574
+ environment,
575
+ filterPattern: Monitoring.FilterPatterns.errors,
576
+ metricTransformations: [
577
+ {
578
+ metricName: 'ErrorCount',
579
+ metricNamespace: 'MyApp/Lambda',
580
+ metricValue: '1',
581
+ },
582
+ ],
583
+ })
584
+
585
+ // Create alarm from metric filter
586
+ const { alarm, logicalId: alarmId } = Monitoring.createAlarm({
587
+ slug,
588
+ environment,
589
+ alarmName: 'lambda-errors',
590
+ metricName: 'ErrorCount',
591
+ namespace: 'MyApp/Lambda',
592
+ threshold: 5,
593
+ comparisonOperator: 'GreaterThanThreshold',
594
+ evaluationPeriods: 2,
595
+ })
596
+
597
+ // Create dashboard
598
+ const { dashboard, logicalId: dashboardId } = Monitoring.createDashboard({
599
+ slug,
600
+ environment,
601
+ widgets: [
602
+ Monitoring.DashboardWidgets.metric(
603
+ 0,
604
+ 0,
605
+ 12,
606
+ 6,
607
+ [['MyApp/Lambda', 'ErrorCount']],
608
+ 'Lambda Errors',
609
+ ),
610
+ Monitoring.DashboardWidgets.log(
611
+ 0,
612
+ 6,
613
+ 12,
614
+ 6,
615
+ ['/aws/lambda/my-function'],
616
+ 'Recent Errors',
617
+ ),
618
+ ],
619
+ })
620
+
621
+ builder.addResource(logGroupId, logGroup)
622
+ builder.addResource(filterId, metricFilter)
623
+ builder.addResource(alarmId, alarm)
624
+ builder.addResource(dashboardId, dashboard)
625
+
626
+ const template = builder.build()
627
+
628
+ expect(Object.keys(template.Resources)).toHaveLength(4)
629
+ expect(template.Resources[logGroupId].Type).toBe('AWS::Logs::LogGroup')
630
+ expect(template.Resources[filterId].Type).toBe('AWS::Logs::MetricFilter')
631
+ expect(template.Resources[alarmId].Type).toBe('AWS::CloudWatch::Alarm')
632
+ expect(template.Resources[dashboardId].Type).toBe('AWS::CloudWatch::Dashboard')
633
+ })
634
+ })