@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,434 @@
1
+ /**
2
+ * Error Handling Tests
3
+ */
4
+
5
+ import { describe, expect, it, beforeEach, afterEach } from 'bun:test'
6
+ import {
7
+ CloudError,
8
+ ConfigurationError,
9
+ CredentialError,
10
+ DeploymentError,
11
+ ValidationError,
12
+ AWSAPIError,
13
+ ErrorCodes,
14
+ getErrorDetails,
15
+ createError,
16
+ DebugLogger,
17
+ validateConfiguration,
18
+ detectMisconfigurations,
19
+ } from './index'
20
+
21
+ describe('CloudError', () => {
22
+ it('should create a CloudError with message, code, and solution', () => {
23
+ const error = new CloudError(
24
+ 'Test error message',
25
+ 'TEST_ERROR',
26
+ 'This is the solution',
27
+ { detail1: 'value1' },
28
+ )
29
+
30
+ expect(error.message).toBe('Test error message')
31
+ expect(error.code).toBe('TEST_ERROR')
32
+ expect(error.solution).toBe('This is the solution')
33
+ expect(error.details).toEqual({ detail1: 'value1' })
34
+ expect(error.name).toBe('CloudError')
35
+ })
36
+
37
+ it('should format toString() with solution and details', () => {
38
+ const error = new CloudError(
39
+ 'Test error',
40
+ 'TEST_ERROR',
41
+ 'Fix it like this',
42
+ { key: 'value' },
43
+ )
44
+
45
+ const str = error.toString()
46
+ expect(str).toContain('CloudError [TEST_ERROR]: Test error')
47
+ expect(str).toContain('💡 Solution: Fix it like this')
48
+ expect(str).toContain('Details:')
49
+ expect(str).toContain('key: "value"')
50
+ })
51
+
52
+ it('should work without solution and details', () => {
53
+ const error = new CloudError('Simple error', 'SIMPLE_ERROR')
54
+
55
+ const str = error.toString()
56
+ expect(str).toBe('CloudError [SIMPLE_ERROR]: Simple error')
57
+ })
58
+ })
59
+
60
+ describe('Specialized Error Classes', () => {
61
+ it('should create ConfigurationError', () => {
62
+ const error = new ConfigurationError('Config is wrong', 'Fix your config')
63
+
64
+ expect(error.name).toBe('ConfigurationError')
65
+ expect(error.code).toBe('CONFIG_ERROR')
66
+ expect(error.message).toBe('Config is wrong')
67
+ expect(error.solution).toBe('Fix your config')
68
+ })
69
+
70
+ it('should create CredentialError', () => {
71
+ const error = new CredentialError('Bad credentials', 'Update your keys')
72
+
73
+ expect(error.name).toBe('CredentialError')
74
+ expect(error.code).toBe('CREDENTIAL_ERROR')
75
+ expect(error.message).toBe('Bad credentials')
76
+ })
77
+
78
+ it('should create DeploymentError', () => {
79
+ const error = new DeploymentError('Deploy failed', 'Check CloudFormation')
80
+
81
+ expect(error.name).toBe('DeploymentError')
82
+ expect(error.code).toBe('DEPLOYMENT_ERROR')
83
+ })
84
+
85
+ it('should create ValidationError', () => {
86
+ const error = new ValidationError('Invalid input', 'Check your data')
87
+
88
+ expect(error.name).toBe('ValidationError')
89
+ expect(error.code).toBe('VALIDATION_ERROR')
90
+ })
91
+
92
+ it('should create AWSAPIError with status code', () => {
93
+ const error = new AWSAPIError('API failed', 403, 'Check permissions')
94
+
95
+ expect(error.name).toBe('AWSAPIError')
96
+ expect(error.code).toBe('AWS_API_ERROR')
97
+ expect(error.statusCode).toBe(403)
98
+ })
99
+ })
100
+
101
+ describe('ErrorCodes', () => {
102
+ it('should have predefined error codes', () => {
103
+ expect(ErrorCodes.NO_CREDENTIALS).toBeDefined()
104
+ expect(ErrorCodes.INVALID_CREDENTIALS).toBeDefined()
105
+ expect(ErrorCodes.MISSING_CONFIG).toBeDefined()
106
+ expect(ErrorCodes.INVALID_REGION).toBeDefined()
107
+ })
108
+
109
+ it('should provide message and solution for each error code', () => {
110
+ const error = ErrorCodes.NO_CREDENTIALS
111
+
112
+ expect(error.message).toBe('AWS credentials not found')
113
+ expect(error.solution).toContain('Configure AWS credentials')
114
+ expect(error.solution).toContain('AWS_ACCESS_KEY_ID')
115
+ })
116
+ })
117
+
118
+ describe('getErrorDetails', () => {
119
+ it('should return error details by code', () => {
120
+ const details = getErrorDetails('INVALID_CREDENTIALS')
121
+
122
+ expect(details.message).toBe('AWS credentials are invalid')
123
+ expect(details.solution).toContain('AWS_ACCESS_KEY_ID')
124
+ })
125
+ })
126
+
127
+ describe('createError', () => {
128
+ it('should create error from error code', () => {
129
+ const error = createError('MISSING_CONFIG', { path: '/path/to/config' })
130
+
131
+ expect(error.message).toBe('Configuration file not found')
132
+ expect(error.code).toBe('MISSING_CONFIG')
133
+ expect(error.solution).toContain('cloud.config.ts')
134
+ expect(error.details).toEqual({ path: '/path/to/config' })
135
+ })
136
+
137
+ it('should work without additional details', () => {
138
+ const error = createError('INVALID_REGION')
139
+
140
+ expect(error.message).toBe('AWS region is invalid')
141
+ expect(error.code).toBe('INVALID_REGION')
142
+ expect(error.details).toBeUndefined()
143
+ })
144
+ })
145
+
146
+ describe('DebugLogger', () => {
147
+ let consoleLog: typeof console.log
148
+ let consoleWarn: typeof console.warn
149
+ let consoleError: typeof console.error
150
+ let logs: string[] = []
151
+
152
+ beforeEach(() => {
153
+ logs = []
154
+ consoleLog = console.log
155
+ consoleWarn = console.warn
156
+ consoleError = console.error
157
+
158
+ console.log = (...args: any[]) => logs.push(args.join(' '))
159
+ console.warn = (...args: any[]) => logs.push(args.join(' '))
160
+ console.error = (...args: any[]) => logs.push(args.join(' '))
161
+
162
+ DebugLogger.setVerbose(false)
163
+ DebugLogger.setDebug(false)
164
+ })
165
+
166
+ afterEach(() => {
167
+ console.log = consoleLog
168
+ console.warn = consoleWarn
169
+ console.error = consoleError
170
+ })
171
+
172
+ it('should log verbose messages when verbose mode is enabled', () => {
173
+ DebugLogger.setVerbose(true)
174
+ DebugLogger.verbose('Verbose message')
175
+
176
+ expect(logs).toContain('[VERBOSE] Verbose message')
177
+ })
178
+
179
+ it('should not log verbose messages when verbose mode is disabled', () => {
180
+ DebugLogger.setVerbose(false)
181
+ DebugLogger.verbose('Verbose message')
182
+
183
+ expect(logs).not.toContain('[VERBOSE] Verbose message')
184
+ })
185
+
186
+ it('should log debug messages when debug mode is enabled', () => {
187
+ DebugLogger.setDebug(true)
188
+ DebugLogger.debug('Debug message')
189
+
190
+ expect(logs).toContain('[DEBUG] Debug message')
191
+ })
192
+
193
+ it('should not log debug messages when debug mode is disabled', () => {
194
+ DebugLogger.setDebug(false)
195
+ DebugLogger.debug('Debug message')
196
+
197
+ expect(logs).not.toContain('[DEBUG] Debug message')
198
+ })
199
+
200
+ it('should always log info messages', () => {
201
+ DebugLogger.info('Info message')
202
+
203
+ expect(logs.some(log => log.includes('Info message'))).toBe(true)
204
+ })
205
+
206
+ it('should always log warning messages', () => {
207
+ DebugLogger.warn('Warning message')
208
+
209
+ expect(logs.some(log => log.includes('Warning message'))).toBe(true)
210
+ })
211
+
212
+ it('should always log error messages', () => {
213
+ DebugLogger.error('Error message')
214
+
215
+ expect(logs.some(log => log.includes('Error message'))).toBe(true)
216
+ })
217
+
218
+ it('should log CloudError with formatted output', () => {
219
+ const error = new CloudError('Test error', 'TEST', 'Test solution')
220
+ DebugLogger.error('Something failed', error)
221
+
222
+ expect(logs.some(log => log.includes('Something failed'))).toBe(true)
223
+ expect(logs.some(log => log.includes('Test error'))).toBe(true)
224
+ })
225
+
226
+ it('should log stack trace in debug mode', () => {
227
+ DebugLogger.setDebug(true)
228
+ const error = new Error('Test error')
229
+ DebugLogger.error('Failed', error)
230
+
231
+ expect(logs.some(log => log.includes('Stack trace'))).toBe(true)
232
+ })
233
+
234
+ it('should log success messages', () => {
235
+ DebugLogger.success('Success message')
236
+
237
+ expect(logs.some(log => log.includes('Success message'))).toBe(true)
238
+ })
239
+ })
240
+
241
+ describe('validateConfiguration', () => {
242
+ it('should throw error if config is null', () => {
243
+ expect(() => validateConfiguration(null)).toThrow()
244
+ })
245
+
246
+ it('should throw error if config is undefined', () => {
247
+ expect(() => validateConfiguration(undefined)).toThrow()
248
+ })
249
+
250
+ it('should throw error if project is missing', () => {
251
+ expect(() => validateConfiguration({})).toThrow(ConfigurationError)
252
+ expect(() => validateConfiguration({})).toThrow('Missing required field: project')
253
+ })
254
+
255
+ it('should throw error if project.name is missing', () => {
256
+ expect(() => validateConfiguration({ project: {} })).toThrow(ConfigurationError)
257
+ expect(() => validateConfiguration({ project: {} })).toThrow('project.name')
258
+ })
259
+
260
+ it('should throw error if project.slug is missing', () => {
261
+ expect(() => validateConfiguration({ project: { name: 'Test' } })).toThrow(ConfigurationError)
262
+ expect(() => validateConfiguration({ project: { name: 'Test' } })).toThrow('project.slug')
263
+ })
264
+
265
+ it('should throw error if slug format is invalid', () => {
266
+ expect(() =>
267
+ validateConfiguration({
268
+ project: { name: 'Test', slug: 'Invalid_Slug' },
269
+ }),
270
+ ).toThrow(ValidationError)
271
+ })
272
+
273
+ it('should accept valid slug format', () => {
274
+ expect(() =>
275
+ validateConfiguration({
276
+ project: { name: 'Test', slug: 'valid-slug-123' },
277
+ }),
278
+ ).not.toThrow()
279
+ })
280
+
281
+ it('should throw error for invalid region', () => {
282
+ expect(() =>
283
+ validateConfiguration({
284
+ project: { name: 'Test', slug: 'test', region: 'invalid-region' },
285
+ }),
286
+ ).toThrow('AWS region is invalid')
287
+ })
288
+
289
+ it('should accept valid regions', () => {
290
+ const validRegions = [
291
+ 'us-east-1',
292
+ 'us-west-2',
293
+ 'eu-west-1',
294
+ 'eu-central-1',
295
+ 'ap-southeast-1',
296
+ ]
297
+
298
+ for (const region of validRegions) {
299
+ expect(() =>
300
+ validateConfiguration({
301
+ project: { name: 'Test', slug: 'test', region },
302
+ }),
303
+ ).not.toThrow()
304
+ }
305
+ })
306
+
307
+ it('should accept config without region', () => {
308
+ expect(() =>
309
+ validateConfiguration({
310
+ project: { name: 'Test', slug: 'test' },
311
+ }),
312
+ ).not.toThrow()
313
+ })
314
+ })
315
+
316
+ describe('detectMisconfigurations', () => {
317
+ it('should return empty array for minimal config', () => {
318
+ const warnings = detectMisconfigurations({
319
+ project: { name: 'Test', slug: 'test' },
320
+ })
321
+
322
+ expect(Array.isArray(warnings)).toBe(true)
323
+ })
324
+
325
+ it('should warn about production database without Multi-AZ', () => {
326
+ const warnings = detectMisconfigurations({
327
+ project: { name: 'Test', slug: 'test' },
328
+ environments: {
329
+ production: {},
330
+ },
331
+ infrastructure: {
332
+ database: {
333
+ postgres: {
334
+ instanceClass: 'db.t3.micro',
335
+ },
336
+ },
337
+ },
338
+ })
339
+
340
+ expect(warnings).toContain('Production database should use Multi-AZ for high availability')
341
+ })
342
+
343
+ it('should not warn if Multi-AZ is enabled', () => {
344
+ const warnings = detectMisconfigurations({
345
+ project: { name: 'Test', slug: 'test' },
346
+ environments: {
347
+ production: {},
348
+ },
349
+ infrastructure: {
350
+ database: {
351
+ postgres: {
352
+ instanceClass: 'db.t3.micro',
353
+ multiAZ: true,
354
+ },
355
+ },
356
+ },
357
+ })
358
+
359
+ expect(warnings).not.toContain(
360
+ 'Production database should use Multi-AZ for high availability',
361
+ )
362
+ })
363
+
364
+ it('should warn about unencrypted storage', () => {
365
+ const warnings = detectMisconfigurations({
366
+ project: { name: 'Test', slug: 'test' },
367
+ infrastructure: {
368
+ storage: {
369
+ uploads: {
370
+ encryption: false,
371
+ },
372
+ },
373
+ },
374
+ })
375
+
376
+ expect(warnings.some(w => w.includes('not encrypted'))).toBe(true)
377
+ })
378
+
379
+ it('should warn about public S3 buckets', () => {
380
+ const warnings = detectMisconfigurations({
381
+ project: { name: 'Test', slug: 'test' },
382
+ infrastructure: {
383
+ storage: {
384
+ assets: {
385
+ public: true,
386
+ },
387
+ },
388
+ },
389
+ })
390
+
391
+ expect(warnings.some(w => w.includes('publicly accessible'))).toBe(true)
392
+ })
393
+
394
+ it('should warn about low backup retention', () => {
395
+ const warnings = detectMisconfigurations({
396
+ project: { name: 'Test', slug: 'test' },
397
+ infrastructure: {
398
+ database: {
399
+ postgres: {
400
+ backupRetentionDays: 3,
401
+ },
402
+ },
403
+ },
404
+ })
405
+
406
+ expect(warnings.some(w => w.includes('backup retention'))).toBe(true)
407
+ })
408
+
409
+ it('should warn about missing monitoring', () => {
410
+ const warnings = detectMisconfigurations({
411
+ project: { name: 'Test', slug: 'test' },
412
+ infrastructure: {
413
+ compute: {
414
+ server: {},
415
+ },
416
+ },
417
+ })
418
+
419
+ expect(warnings.some(w => w.includes('No monitoring configured'))).toBe(true)
420
+ })
421
+
422
+ it('should not warn if monitoring is configured', () => {
423
+ const warnings = detectMisconfigurations({
424
+ project: { name: 'Test', slug: 'test' },
425
+ infrastructure: {
426
+ monitoring: {
427
+ alarms: [],
428
+ },
429
+ },
430
+ })
431
+
432
+ expect(warnings).not.toContain('No monitoring configured')
433
+ })
434
+ })