@friggframework/devtools 2.0.0-next.45 → 2.0.0-next.46

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 (212) hide show
  1. package/infrastructure/ARCHITECTURE.md +487 -0
  2. package/infrastructure/HEALTH.md +468 -0
  3. package/infrastructure/README.md +51 -0
  4. package/infrastructure/__tests__/postgres-config.test.js +914 -0
  5. package/infrastructure/__tests__/template-generation.test.js +687 -0
  6. package/infrastructure/create-frigg-infrastructure.js +1 -1
  7. package/infrastructure/docs/POSTGRES-CONFIGURATION.md +630 -0
  8. package/infrastructure/{DEPLOYMENT-INSTRUCTIONS.md → docs/deployment-instructions.md} +3 -3
  9. package/infrastructure/{IAM-POLICY-TEMPLATES.md → docs/iam-policy-templates.md} +9 -10
  10. package/infrastructure/domains/database/aurora-builder.js +809 -0
  11. package/infrastructure/domains/database/aurora-builder.test.js +950 -0
  12. package/infrastructure/domains/database/aurora-discovery.js +87 -0
  13. package/infrastructure/domains/database/aurora-discovery.test.js +188 -0
  14. package/infrastructure/domains/database/aurora-resolver.js +210 -0
  15. package/infrastructure/domains/database/aurora-resolver.test.js +347 -0
  16. package/infrastructure/domains/database/migration-builder.js +633 -0
  17. package/infrastructure/domains/database/migration-builder.test.js +294 -0
  18. package/infrastructure/domains/database/migration-resolver.js +163 -0
  19. package/infrastructure/domains/database/migration-resolver.test.js +337 -0
  20. package/infrastructure/domains/health/application/ports/IPropertyReconciler.js +164 -0
  21. package/infrastructure/domains/health/application/ports/IResourceDetector.js +129 -0
  22. package/infrastructure/domains/health/application/ports/IResourceImporter.js +142 -0
  23. package/infrastructure/domains/health/application/ports/IStackRepository.js +131 -0
  24. package/infrastructure/domains/health/application/ports/index.js +26 -0
  25. package/infrastructure/domains/health/application/use-cases/__tests__/execute-resource-import-use-case.test.js +679 -0
  26. package/infrastructure/domains/health/application/use-cases/__tests__/mismatch-analyzer-method-name.test.js +167 -0
  27. package/infrastructure/domains/health/application/use-cases/__tests__/repair-via-import-use-case.test.js +1130 -0
  28. package/infrastructure/domains/health/application/use-cases/execute-resource-import-use-case.js +221 -0
  29. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.js +152 -0
  30. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.test.js +343 -0
  31. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.js +535 -0
  32. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.test.js +376 -0
  33. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.js +213 -0
  34. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.test.js +441 -0
  35. package/infrastructure/domains/health/docs/ACME-DEV-DRIFT-ANALYSIS.md +267 -0
  36. package/infrastructure/domains/health/docs/BUILD-VS-DEPLOYED-TEMPLATE-ANALYSIS.md +324 -0
  37. package/infrastructure/domains/health/docs/ORPHAN-DETECTION-ANALYSIS.md +386 -0
  38. package/infrastructure/domains/health/docs/SPEC-CLEANUP-COMMAND.md +1419 -0
  39. package/infrastructure/domains/health/docs/TDD-IMPLEMENTATION-SUMMARY.md +391 -0
  40. package/infrastructure/domains/health/docs/TEMPLATE-COMPARISON-IMPLEMENTATION.md +551 -0
  41. package/infrastructure/domains/health/domain/entities/issue.js +299 -0
  42. package/infrastructure/domains/health/domain/entities/issue.test.js +528 -0
  43. package/infrastructure/domains/health/domain/entities/property-mismatch.js +108 -0
  44. package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +275 -0
  45. package/infrastructure/domains/health/domain/entities/resource.js +159 -0
  46. package/infrastructure/domains/health/domain/entities/resource.test.js +432 -0
  47. package/infrastructure/domains/health/domain/entities/stack-health-report.js +306 -0
  48. package/infrastructure/domains/health/domain/entities/stack-health-report.test.js +601 -0
  49. package/infrastructure/domains/health/domain/services/__tests__/health-score-percentage-based.test.js +380 -0
  50. package/infrastructure/domains/health/domain/services/__tests__/import-progress-monitor.test.js +971 -0
  51. package/infrastructure/domains/health/domain/services/__tests__/import-template-generator.test.js +1150 -0
  52. package/infrastructure/domains/health/domain/services/__tests__/logical-id-mapper.test.js +672 -0
  53. package/infrastructure/domains/health/domain/services/__tests__/template-parser.test.js +496 -0
  54. package/infrastructure/domains/health/domain/services/__tests__/update-progress-monitor.test.js +419 -0
  55. package/infrastructure/domains/health/domain/services/health-score-calculator.js +248 -0
  56. package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +504 -0
  57. package/infrastructure/domains/health/domain/services/import-progress-monitor.js +195 -0
  58. package/infrastructure/domains/health/domain/services/import-template-generator.js +435 -0
  59. package/infrastructure/domains/health/domain/services/logical-id-mapper.js +345 -0
  60. package/infrastructure/domains/health/domain/services/mismatch-analyzer.js +234 -0
  61. package/infrastructure/domains/health/domain/services/mismatch-analyzer.test.js +431 -0
  62. package/infrastructure/domains/health/domain/services/property-mutability-config.js +382 -0
  63. package/infrastructure/domains/health/domain/services/template-parser.js +245 -0
  64. package/infrastructure/domains/health/domain/services/update-progress-monitor.js +192 -0
  65. package/infrastructure/domains/health/domain/value-objects/health-score.js +138 -0
  66. package/infrastructure/domains/health/domain/value-objects/health-score.test.js +267 -0
  67. package/infrastructure/domains/health/domain/value-objects/property-mutability.js +161 -0
  68. package/infrastructure/domains/health/domain/value-objects/property-mutability.test.js +198 -0
  69. package/infrastructure/domains/health/domain/value-objects/resource-state.js +167 -0
  70. package/infrastructure/domains/health/domain/value-objects/resource-state.test.js +196 -0
  71. package/infrastructure/domains/health/domain/value-objects/stack-identifier.js +192 -0
  72. package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +262 -0
  73. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-cfn-tagged.test.js +312 -0
  74. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-multi-stack.test.js +367 -0
  75. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-relationship-analysis.test.js +432 -0
  76. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.js +784 -0
  77. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.test.js +1133 -0
  78. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.js +565 -0
  79. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +554 -0
  80. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.js +318 -0
  81. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.test.js +398 -0
  82. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +777 -0
  83. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.test.js +580 -0
  84. package/infrastructure/domains/integration/integration-builder.js +397 -0
  85. package/infrastructure/domains/integration/integration-builder.test.js +593 -0
  86. package/infrastructure/domains/integration/integration-resolver.js +170 -0
  87. package/infrastructure/domains/integration/integration-resolver.test.js +369 -0
  88. package/infrastructure/domains/integration/websocket-builder.js +69 -0
  89. package/infrastructure/domains/integration/websocket-builder.test.js +195 -0
  90. package/infrastructure/domains/networking/vpc-builder.js +1829 -0
  91. package/infrastructure/domains/networking/vpc-builder.test.js +1262 -0
  92. package/infrastructure/domains/networking/vpc-discovery.js +177 -0
  93. package/infrastructure/domains/networking/vpc-discovery.test.js +350 -0
  94. package/infrastructure/domains/networking/vpc-resolver.js +324 -0
  95. package/infrastructure/domains/networking/vpc-resolver.test.js +501 -0
  96. package/infrastructure/domains/parameters/ssm-builder.js +79 -0
  97. package/infrastructure/domains/parameters/ssm-builder.test.js +189 -0
  98. package/infrastructure/domains/parameters/ssm-discovery.js +84 -0
  99. package/infrastructure/domains/parameters/ssm-discovery.test.js +210 -0
  100. package/infrastructure/{iam-generator.js → domains/security/iam-generator.js} +2 -2
  101. package/infrastructure/domains/security/kms-builder.js +366 -0
  102. package/infrastructure/domains/security/kms-builder.test.js +374 -0
  103. package/infrastructure/domains/security/kms-discovery.js +80 -0
  104. package/infrastructure/domains/security/kms-discovery.test.js +177 -0
  105. package/infrastructure/domains/security/kms-resolver.js +96 -0
  106. package/infrastructure/domains/security/kms-resolver.test.js +216 -0
  107. package/infrastructure/domains/shared/base-builder.js +112 -0
  108. package/infrastructure/domains/shared/base-resolver.js +186 -0
  109. package/infrastructure/domains/shared/base-resolver.test.js +305 -0
  110. package/infrastructure/domains/shared/builder-orchestrator.js +212 -0
  111. package/infrastructure/domains/shared/builder-orchestrator.test.js +213 -0
  112. package/infrastructure/domains/shared/cloudformation-discovery-v2.js +334 -0
  113. package/infrastructure/domains/shared/cloudformation-discovery.js +375 -0
  114. package/infrastructure/domains/shared/cloudformation-discovery.test.js +590 -0
  115. package/infrastructure/domains/shared/environment-builder.js +119 -0
  116. package/infrastructure/domains/shared/environment-builder.test.js +247 -0
  117. package/infrastructure/domains/shared/providers/aws-provider-adapter.js +544 -0
  118. package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +377 -0
  119. package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +93 -0
  120. package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +136 -0
  121. package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +82 -0
  122. package/infrastructure/domains/shared/providers/provider-factory.js +108 -0
  123. package/infrastructure/domains/shared/providers/provider-factory.test.js +170 -0
  124. package/infrastructure/domains/shared/resource-discovery.js +192 -0
  125. package/infrastructure/domains/shared/resource-discovery.test.js +552 -0
  126. package/infrastructure/domains/shared/types/app-definition.js +205 -0
  127. package/infrastructure/domains/shared/types/discovery-result.js +106 -0
  128. package/infrastructure/domains/shared/types/discovery-result.test.js +258 -0
  129. package/infrastructure/domains/shared/types/index.js +46 -0
  130. package/infrastructure/domains/shared/types/resource-ownership.js +108 -0
  131. package/infrastructure/domains/shared/types/resource-ownership.test.js +101 -0
  132. package/infrastructure/domains/shared/utilities/base-definition-factory.js +380 -0
  133. package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
  134. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +248 -0
  135. package/infrastructure/domains/shared/utilities/handler-path-resolver.js +134 -0
  136. package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +268 -0
  137. package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +55 -0
  138. package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +138 -0
  139. package/infrastructure/{env-validator.js → domains/shared/validation/env-validator.js} +2 -1
  140. package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
  141. package/infrastructure/esbuild.config.js +53 -0
  142. package/infrastructure/infrastructure-composer.js +87 -0
  143. package/infrastructure/{serverless-template.test.js → infrastructure-composer.test.js} +115 -24
  144. package/infrastructure/scripts/build-prisma-layer.js +553 -0
  145. package/infrastructure/scripts/build-prisma-layer.test.js +102 -0
  146. package/infrastructure/{build-time-discovery.js → scripts/build-time-discovery.js} +80 -48
  147. package/infrastructure/{build-time-discovery.test.js → scripts/build-time-discovery.test.js} +5 -4
  148. package/layers/prisma/nodejs/package.json +8 -0
  149. package/management-ui/server/utils/cliIntegration.js +1 -1
  150. package/management-ui/server/utils/environment/awsParameterStore.js +29 -18
  151. package/package.json +11 -11
  152. package/frigg-cli/.eslintrc.js +0 -141
  153. package/frigg-cli/__tests__/unit/commands/build.test.js +0 -251
  154. package/frigg-cli/__tests__/unit/commands/db-setup.test.js +0 -548
  155. package/frigg-cli/__tests__/unit/commands/install.test.js +0 -400
  156. package/frigg-cli/__tests__/unit/commands/ui.test.js +0 -346
  157. package/frigg-cli/__tests__/unit/utils/database-validator.test.js +0 -366
  158. package/frigg-cli/__tests__/unit/utils/error-messages.test.js +0 -304
  159. package/frigg-cli/__tests__/unit/utils/prisma-runner.test.js +0 -486
  160. package/frigg-cli/__tests__/utils/mock-factory.js +0 -270
  161. package/frigg-cli/__tests__/utils/prisma-mock.js +0 -194
  162. package/frigg-cli/__tests__/utils/test-fixtures.js +0 -463
  163. package/frigg-cli/__tests__/utils/test-setup.js +0 -287
  164. package/frigg-cli/build-command/index.js +0 -65
  165. package/frigg-cli/db-setup-command/index.js +0 -193
  166. package/frigg-cli/deploy-command/index.js +0 -175
  167. package/frigg-cli/generate-command/__tests__/generate-command.test.js +0 -301
  168. package/frigg-cli/generate-command/azure-generator.js +0 -43
  169. package/frigg-cli/generate-command/gcp-generator.js +0 -47
  170. package/frigg-cli/generate-command/index.js +0 -332
  171. package/frigg-cli/generate-command/terraform-generator.js +0 -555
  172. package/frigg-cli/generate-iam-command.js +0 -118
  173. package/frigg-cli/index.js +0 -75
  174. package/frigg-cli/index.test.js +0 -158
  175. package/frigg-cli/init-command/backend-first-handler.js +0 -756
  176. package/frigg-cli/init-command/index.js +0 -93
  177. package/frigg-cli/init-command/template-handler.js +0 -143
  178. package/frigg-cli/install-command/backend-js.js +0 -33
  179. package/frigg-cli/install-command/commit-changes.js +0 -16
  180. package/frigg-cli/install-command/environment-variables.js +0 -127
  181. package/frigg-cli/install-command/environment-variables.test.js +0 -136
  182. package/frigg-cli/install-command/index.js +0 -54
  183. package/frigg-cli/install-command/install-package.js +0 -13
  184. package/frigg-cli/install-command/integration-file.js +0 -30
  185. package/frigg-cli/install-command/logger.js +0 -12
  186. package/frigg-cli/install-command/template.js +0 -90
  187. package/frigg-cli/install-command/validate-package.js +0 -75
  188. package/frigg-cli/jest.config.js +0 -124
  189. package/frigg-cli/package.json +0 -54
  190. package/frigg-cli/start-command/index.js +0 -149
  191. package/frigg-cli/start-command/start-command.test.js +0 -297
  192. package/frigg-cli/test/init-command.test.js +0 -180
  193. package/frigg-cli/test/npm-registry.test.js +0 -319
  194. package/frigg-cli/ui-command/index.js +0 -154
  195. package/frigg-cli/utils/app-resolver.js +0 -319
  196. package/frigg-cli/utils/backend-path.js +0 -25
  197. package/frigg-cli/utils/database-validator.js +0 -161
  198. package/frigg-cli/utils/error-messages.js +0 -257
  199. package/frigg-cli/utils/npm-registry.js +0 -167
  200. package/frigg-cli/utils/prisma-runner.js +0 -280
  201. package/frigg-cli/utils/process-manager.js +0 -199
  202. package/frigg-cli/utils/repo-detection.js +0 -405
  203. package/infrastructure/aws-discovery.js +0 -1176
  204. package/infrastructure/aws-discovery.test.js +0 -1220
  205. package/infrastructure/serverless-template.js +0 -2094
  206. /package/infrastructure/{WEBSOCKET-CONFIGURATION.md → docs/WEBSOCKET-CONFIGURATION.md} +0 -0
  207. /package/infrastructure/{GENERATE-IAM-DOCS.md → docs/generate-iam-command.md} +0 -0
  208. /package/infrastructure/{iam-generator.test.js → domains/security/iam-generator.test.js} +0 -0
  209. /package/infrastructure/{frigg-deployment-iam-stack.yaml → domains/security/templates/frigg-deployment-iam-stack.yaml} +0 -0
  210. /package/infrastructure/{iam-policy-basic.json → domains/security/templates/iam-policy-basic.json} +0 -0
  211. /package/infrastructure/{iam-policy-full.json → domains/security/templates/iam-policy-full.json} +0 -0
  212. /package/infrastructure/{run-discovery.js → scripts/run-discovery.js} +0 -0
@@ -0,0 +1,398 @@
1
+ /**
2
+ * Tests for AWSResourceImporter Adapter
3
+ *
4
+ * Tests CloudFormation resource import operations using mocked AWS SDK
5
+ */
6
+
7
+ const AWSResourceImporter = require('./aws-resource-importer');
8
+ const StackIdentifier = require('../../domain/value-objects/stack-identifier');
9
+
10
+ // Mock AWS SDK
11
+ jest.mock('@aws-sdk/client-cloudformation', () => ({
12
+ CloudFormationClient: jest.fn(),
13
+ CreateChangeSetCommand: jest.fn(),
14
+ DescribeChangeSetCommand: jest.fn(),
15
+ ExecuteChangeSetCommand: jest.fn(),
16
+ GetTemplateCommand: jest.fn(),
17
+ }));
18
+
19
+ describe('AWSResourceImporter', () => {
20
+ let importer;
21
+ let mockSend;
22
+
23
+ beforeEach(() => {
24
+ jest.clearAllMocks();
25
+
26
+ mockSend = jest.fn();
27
+ const { CloudFormationClient } = require('@aws-sdk/client-cloudformation');
28
+ CloudFormationClient.mockImplementation(() => ({ send: mockSend }));
29
+
30
+ importer = new AWSResourceImporter({ region: 'us-east-1' });
31
+ });
32
+
33
+ describe('supportsImport', () => {
34
+ it('should return true for VPC', async () => {
35
+ const supports = await importer.supportsImport('AWS::EC2::VPC');
36
+ expect(supports).toBe(true);
37
+ });
38
+
39
+ it('should return true for RDS DBCluster', async () => {
40
+ const supports = await importer.supportsImport('AWS::RDS::DBCluster');
41
+ expect(supports).toBe(true);
42
+ });
43
+
44
+ it('should return true for KMS Key', async () => {
45
+ const supports = await importer.supportsImport('AWS::KMS::Key');
46
+ expect(supports).toBe(true);
47
+ });
48
+
49
+ it('should return false for unsupported type', async () => {
50
+ const supports = await importer.supportsImport('AWS::Lambda::Function');
51
+ expect(supports).toBe(false);
52
+ });
53
+ });
54
+
55
+ describe('getIdentifierProperty', () => {
56
+ it('should return VpcId for VPC', async () => {
57
+ const prop = await importer.getIdentifierProperty('AWS::EC2::VPC');
58
+ expect(prop).toBe('VpcId');
59
+ });
60
+
61
+ it('should return SubnetId for Subnet', async () => {
62
+ const prop = await importer.getIdentifierProperty('AWS::EC2::Subnet');
63
+ expect(prop).toBe('SubnetId');
64
+ });
65
+
66
+ it('should return DBClusterIdentifier for RDS DBCluster', async () => {
67
+ const prop = await importer.getIdentifierProperty('AWS::RDS::DBCluster');
68
+ expect(prop).toBe('DBClusterIdentifier');
69
+ });
70
+
71
+ it('should throw error for unsupported type', async () => {
72
+ await expect(
73
+ importer.getIdentifierProperty('AWS::Lambda::Function')
74
+ ).rejects.toThrow('Resource type AWS::Lambda::Function does not support import');
75
+ });
76
+ });
77
+
78
+ describe('validateImport', () => {
79
+ it('should validate VPC import', async () => {
80
+ const result = await importer.validateImport({
81
+ resourceType: 'AWS::EC2::VPC',
82
+ physicalId: 'vpc-123',
83
+ region: 'us-east-1',
84
+ });
85
+
86
+ expect(result).toEqual({
87
+ canImport: true,
88
+ reason: '',
89
+ warnings: [],
90
+ });
91
+ });
92
+
93
+ it('should fail validation for unsupported type', async () => {
94
+ const result = await importer.validateImport({
95
+ resourceType: 'AWS::Lambda::Function',
96
+ physicalId: 'my-function',
97
+ region: 'us-east-1',
98
+ });
99
+
100
+ expect(result.canImport).toBe(false);
101
+ expect(result.reason).toContain('not supported');
102
+ });
103
+
104
+ it('should warn about missing required properties', async () => {
105
+ const result = await importer.validateImport({
106
+ resourceType: 'AWS::RDS::DBCluster',
107
+ physicalId: 'my-cluster',
108
+ region: 'us-east-1',
109
+ });
110
+
111
+ expect(result.canImport).toBe(true);
112
+ expect(result.warnings).toContain(
113
+ 'Ensure DBCluster has required properties (Engine, MasterUsername, etc.)'
114
+ );
115
+ });
116
+ });
117
+
118
+ describe('importResource', () => {
119
+ it('should import a single VPC resource', async () => {
120
+ const stackIdentifier = new StackIdentifier({
121
+ stackName: 'my-app-prod',
122
+ region: 'us-east-1',
123
+ });
124
+
125
+ // Mock CreateChangeSet
126
+ mockSend.mockResolvedValueOnce({
127
+ Id: 'changeset-123',
128
+ StackId: 'arn:aws:cloudformation:us-east-1:123456789012:stack/my-app-prod/guid',
129
+ });
130
+
131
+ // Mock ExecuteChangeSet
132
+ mockSend.mockResolvedValueOnce({});
133
+
134
+ const result = await importer.importResource({
135
+ stackIdentifier,
136
+ logicalId: 'ImportedVPC',
137
+ resourceType: 'AWS::EC2::VPC',
138
+ physicalId: 'vpc-123',
139
+ properties: {
140
+ CidrBlock: '10.0.0.0/16',
141
+ EnableDnsSupport: true,
142
+ },
143
+ });
144
+
145
+ expect(result).toEqual({
146
+ operationId: 'changeset-123',
147
+ status: 'IN_PROGRESS',
148
+ message: 'Resource import initiated via change set changeset-123',
149
+ });
150
+
151
+ expect(mockSend).toHaveBeenCalledTimes(2);
152
+ });
153
+
154
+ it('should throw error for unsupported resource type', async () => {
155
+ const stackIdentifier = new StackIdentifier({
156
+ stackName: 'my-app-prod',
157
+ region: 'us-east-1',
158
+ });
159
+
160
+ await expect(
161
+ importer.importResource({
162
+ stackIdentifier,
163
+ logicalId: 'MyFunction',
164
+ resourceType: 'AWS::Lambda::Function',
165
+ physicalId: 'my-function',
166
+ properties: {},
167
+ })
168
+ ).rejects.toThrow('Resource type AWS::Lambda::Function does not support import');
169
+ });
170
+
171
+ it('should handle import failure', async () => {
172
+ const stackIdentifier = new StackIdentifier({
173
+ stackName: 'my-app-prod',
174
+ region: 'us-east-1',
175
+ });
176
+
177
+ const error = new Error('Resource already exists in stack');
178
+ error.name = 'AlreadyExistsException';
179
+ mockSend.mockRejectedValue(error);
180
+
181
+ await expect(
182
+ importer.importResource({
183
+ stackIdentifier,
184
+ logicalId: 'ExistingVPC',
185
+ resourceType: 'AWS::EC2::VPC',
186
+ physicalId: 'vpc-123',
187
+ properties: { CidrBlock: '10.0.0.0/16' },
188
+ })
189
+ ).rejects.toThrow('Resource already exists in stack');
190
+ });
191
+ });
192
+
193
+ describe('importMultipleResources', () => {
194
+ it('should import multiple resources in single operation', async () => {
195
+ const stackIdentifier = new StackIdentifier({
196
+ stackName: 'my-app-prod',
197
+ region: 'us-east-1',
198
+ });
199
+
200
+ const resources = [
201
+ {
202
+ logicalId: 'ImportedVPC',
203
+ resourceType: 'AWS::EC2::VPC',
204
+ physicalId: 'vpc-123',
205
+ properties: { CidrBlock: '10.0.0.0/16' },
206
+ },
207
+ {
208
+ logicalId: 'ImportedSubnet',
209
+ resourceType: 'AWS::EC2::Subnet',
210
+ physicalId: 'subnet-456',
211
+ properties: { VpcId: 'vpc-123', CidrBlock: '10.0.1.0/24' },
212
+ },
213
+ ];
214
+
215
+ // Mock CreateChangeSet
216
+ mockSend.mockResolvedValueOnce({
217
+ Id: 'changeset-multi-123',
218
+ StackId: 'arn:aws:cloudformation:us-east-1:123456789012:stack/my-app-prod/guid',
219
+ });
220
+
221
+ // Mock ExecuteChangeSet
222
+ mockSend.mockResolvedValueOnce({});
223
+
224
+ const result = await importer.importMultipleResources({
225
+ stackIdentifier,
226
+ resources,
227
+ });
228
+
229
+ expect(result).toEqual({
230
+ operationId: 'changeset-multi-123',
231
+ status: 'IN_PROGRESS',
232
+ importedCount: 2,
233
+ failedCount: 0,
234
+ message: 'Import operation initiated for 2 resources',
235
+ details: [],
236
+ });
237
+ });
238
+
239
+ it('should filter out unsupported resource types', async () => {
240
+ const stackIdentifier = new StackIdentifier({
241
+ stackName: 'my-app-prod',
242
+ region: 'us-east-1',
243
+ });
244
+
245
+ const resources = [
246
+ {
247
+ logicalId: 'ImportedVPC',
248
+ resourceType: 'AWS::EC2::VPC',
249
+ physicalId: 'vpc-123',
250
+ properties: { CidrBlock: '10.0.0.0/16' },
251
+ },
252
+ {
253
+ logicalId: 'UnsupportedFunction',
254
+ resourceType: 'AWS::Lambda::Function',
255
+ physicalId: 'my-function',
256
+ properties: {},
257
+ },
258
+ ];
259
+
260
+ // Mock CreateChangeSet (only for VPC)
261
+ mockSend.mockResolvedValueOnce({
262
+ Id: 'changeset-filtered-123',
263
+ StackId: 'arn:aws:cloudformation:us-east-1:123456789012:stack/my-app-prod/guid',
264
+ });
265
+
266
+ // Mock ExecuteChangeSet
267
+ mockSend.mockResolvedValueOnce({});
268
+
269
+ const result = await importer.importMultipleResources({
270
+ stackIdentifier,
271
+ resources,
272
+ });
273
+
274
+ expect(result.importedCount).toBe(1);
275
+ expect(result.failedCount).toBe(1);
276
+ });
277
+ });
278
+
279
+ describe('getImportStatus', () => {
280
+ it('should get status of in-progress import', async () => {
281
+ mockSend.mockResolvedValue({
282
+ Status: 'CREATE_PENDING',
283
+ ExecutionStatus: 'AVAILABLE',
284
+ StatusReason: 'Change set created',
285
+ });
286
+
287
+ const status = await importer.getImportStatus('changeset-123');
288
+
289
+ expect(status).toEqual({
290
+ operationId: 'changeset-123',
291
+ status: 'IN_PROGRESS',
292
+ progress: 25,
293
+ message: 'Change set created',
294
+ completedTime: null,
295
+ });
296
+ });
297
+
298
+ it('should get status of completed import', async () => {
299
+ const completedTime = new Date('2024-01-15T10:30:00Z');
300
+
301
+ mockSend.mockResolvedValue({
302
+ Status: 'CREATE_COMPLETE',
303
+ ExecutionStatus: 'EXECUTE_COMPLETE',
304
+ StatusReason: 'Import completed successfully',
305
+ CreationTime: completedTime,
306
+ });
307
+
308
+ const status = await importer.getImportStatus('changeset-123');
309
+
310
+ expect(status.status).toBe('COMPLETE');
311
+ expect(status.progress).toBe(100);
312
+ expect(status.completedTime).toEqual(completedTime);
313
+ });
314
+
315
+ it('should get status of failed import', async () => {
316
+ mockSend.mockResolvedValue({
317
+ Status: 'FAILED',
318
+ ExecutionStatus: 'EXECUTE_FAILED',
319
+ StatusReason: 'Resource already exists',
320
+ });
321
+
322
+ const status = await importer.getImportStatus('changeset-123');
323
+
324
+ expect(status.status).toBe('FAILED');
325
+ expect(status.message).toContain('already exists');
326
+ });
327
+ });
328
+
329
+ describe('generateTemplateSnippet', () => {
330
+ it('should generate VPC template snippet', async () => {
331
+ const snippet = await importer.generateTemplateSnippet({
332
+ logicalId: 'ImportedVPC',
333
+ resourceType: 'AWS::EC2::VPC',
334
+ properties: {
335
+ CidrBlock: '10.0.0.0/16',
336
+ EnableDnsSupport: true,
337
+ EnableDnsHostnames: true,
338
+ },
339
+ });
340
+
341
+ expect(snippet).toEqual({
342
+ ImportedVPC: {
343
+ Type: 'AWS::EC2::VPC',
344
+ Properties: {
345
+ CidrBlock: '10.0.0.0/16',
346
+ EnableDnsSupport: true,
347
+ EnableDnsHostnames: true,
348
+ },
349
+ },
350
+ });
351
+ });
352
+
353
+ it('should generate RDS DBCluster template snippet', async () => {
354
+ const snippet = await importer.generateTemplateSnippet({
355
+ logicalId: 'ImportedCluster',
356
+ resourceType: 'AWS::RDS::DBCluster',
357
+ properties: {
358
+ Engine: 'aurora-postgresql',
359
+ EngineVersion: '13.7',
360
+ MasterUsername: 'admin',
361
+ },
362
+ });
363
+
364
+ expect(snippet).toEqual({
365
+ ImportedCluster: {
366
+ Type: 'AWS::RDS::DBCluster',
367
+ Properties: {
368
+ Engine: 'aurora-postgresql',
369
+ EngineVersion: '13.7',
370
+ MasterUsername: 'admin',
371
+ },
372
+ },
373
+ });
374
+ });
375
+
376
+ it('should throw error for unsupported resource type', async () => {
377
+ await expect(
378
+ importer.generateTemplateSnippet({
379
+ logicalId: 'MyFunction',
380
+ resourceType: 'AWS::Lambda::Function',
381
+ properties: {},
382
+ })
383
+ ).rejects.toThrow('Resource type AWS::Lambda::Function does not support import');
384
+ });
385
+ });
386
+
387
+ describe('constructor', () => {
388
+ it('should create instance with default region', () => {
389
+ const imp = new AWSResourceImporter();
390
+ expect(imp).toBeInstanceOf(AWSResourceImporter);
391
+ });
392
+
393
+ it('should create instance with custom region', () => {
394
+ const imp = new AWSResourceImporter({ region: 'eu-west-1' });
395
+ expect(imp).toBeInstanceOf(AWSResourceImporter);
396
+ });
397
+ });
398
+ });