@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,343 @@
1
+ /**
2
+ * Tests for ReconcilePropertiesUseCase
3
+ *
4
+ * Use case for reconciling property drift between CloudFormation template
5
+ * and actual cloud resources (frigg repair --reconcile command)
6
+ */
7
+
8
+ const ReconcilePropertiesUseCase = require('./reconcile-properties-use-case');
9
+ const StackIdentifier = require('../../domain/value-objects/stack-identifier');
10
+ const PropertyMismatch = require('../../domain/entities/property-mismatch');
11
+ const PropertyMutability = require('../../domain/value-objects/property-mutability');
12
+
13
+ describe('ReconcilePropertiesUseCase', () => {
14
+ let useCase;
15
+ let mockPropertyReconciler;
16
+
17
+ beforeEach(() => {
18
+ // Mock property reconciler
19
+ mockPropertyReconciler = {
20
+ canReconcile: jest.fn(),
21
+ reconcileProperty: jest.fn(),
22
+ reconcileMultipleProperties: jest.fn(),
23
+ previewReconciliation: jest.fn(),
24
+ };
25
+
26
+ useCase = new ReconcilePropertiesUseCase({
27
+ propertyReconciler: mockPropertyReconciler,
28
+ });
29
+ });
30
+
31
+ describe('reconcileSingleProperty', () => {
32
+ it('should reconcile a single mutable property mismatch', async () => {
33
+ const stackIdentifier = new StackIdentifier({
34
+ stackName: 'my-app-prod',
35
+ region: 'us-east-1',
36
+ });
37
+
38
+ const mismatch = new PropertyMismatch({
39
+ propertyPath: 'Properties.EnableDnsSupport',
40
+ expectedValue: true,
41
+ actualValue: false,
42
+ mutability: PropertyMutability.MUTABLE,
43
+ });
44
+
45
+ // Mock reconciler can handle this mismatch
46
+ mockPropertyReconciler.canReconcile.mockResolvedValue(true);
47
+
48
+ // Mock reconciliation result
49
+ mockPropertyReconciler.reconcileProperty.mockResolvedValue({
50
+ success: true,
51
+ mode: 'template',
52
+ propertyPath: 'Properties.EnableDnsSupport',
53
+ oldValue: true,
54
+ newValue: false,
55
+ message: 'Template updated to match actual resource state',
56
+ });
57
+
58
+ const result = await useCase.reconcileSingleProperty({
59
+ stackIdentifier,
60
+ logicalId: 'MyVPC',
61
+ mismatch,
62
+ mode: 'template',
63
+ });
64
+
65
+ expect(result.success).toBe(true);
66
+ expect(result.mode).toBe('template');
67
+ expect(result.propertyPath).toBe('Properties.EnableDnsSupport');
68
+ expect(mockPropertyReconciler.canReconcile).toHaveBeenCalledWith(mismatch);
69
+ expect(mockPropertyReconciler.reconcileProperty).toHaveBeenCalled();
70
+ });
71
+
72
+ it('should fail if property cannot be reconciled', async () => {
73
+ const stackIdentifier = new StackIdentifier({
74
+ stackName: 'my-app-prod',
75
+ region: 'us-east-1',
76
+ });
77
+
78
+ const mismatch = new PropertyMismatch({
79
+ propertyPath: 'Properties.CidrBlock',
80
+ expectedValue: '10.0.0.0/16',
81
+ actualValue: '10.1.0.0/16',
82
+ mutability: PropertyMutability.IMMUTABLE,
83
+ });
84
+
85
+ // Mock reconciler cannot handle immutable property
86
+ mockPropertyReconciler.canReconcile.mockResolvedValue(false);
87
+
88
+ await expect(
89
+ useCase.reconcileSingleProperty({
90
+ stackIdentifier,
91
+ logicalId: 'MyVPC',
92
+ mismatch,
93
+ mode: 'template',
94
+ })
95
+ ).rejects.toThrow('Property Properties.CidrBlock cannot be reconciled automatically');
96
+ });
97
+
98
+ it('should use resource mode when specified', async () => {
99
+ const stackIdentifier = new StackIdentifier({
100
+ stackName: 'my-app-prod',
101
+ region: 'us-east-1',
102
+ });
103
+
104
+ const mismatch = new PropertyMismatch({
105
+ propertyPath: 'Properties.EnableDnsSupport',
106
+ expectedValue: true,
107
+ actualValue: false,
108
+ mutability: PropertyMutability.MUTABLE,
109
+ });
110
+
111
+ mockPropertyReconciler.canReconcile.mockResolvedValue(true);
112
+
113
+ mockPropertyReconciler.reconcileProperty.mockResolvedValue({
114
+ success: true,
115
+ mode: 'resource',
116
+ propertyPath: 'Properties.EnableDnsSupport',
117
+ oldValue: false,
118
+ newValue: true,
119
+ message: 'Resource updated to match template definition',
120
+ });
121
+
122
+ const result = await useCase.reconcileSingleProperty({
123
+ stackIdentifier,
124
+ logicalId: 'MyVPC',
125
+ mismatch,
126
+ mode: 'resource',
127
+ });
128
+
129
+ expect(result.success).toBe(true);
130
+ expect(result.mode).toBe('resource');
131
+ expect(mockPropertyReconciler.reconcileProperty).toHaveBeenCalledWith({
132
+ stackIdentifier,
133
+ logicalId: 'MyVPC',
134
+ mismatch,
135
+ mode: 'resource',
136
+ });
137
+ });
138
+ });
139
+
140
+ describe('reconcileMultipleProperties', () => {
141
+ it('should reconcile multiple property mismatches for a resource', async () => {
142
+ const stackIdentifier = new StackIdentifier({
143
+ stackName: 'my-app-prod',
144
+ region: 'us-east-1',
145
+ });
146
+
147
+ const mismatches = [
148
+ new PropertyMismatch({
149
+ propertyPath: 'Properties.EnableDnsSupport',
150
+ expectedValue: true,
151
+ actualValue: false,
152
+ mutability: PropertyMutability.MUTABLE,
153
+ }),
154
+ new PropertyMismatch({
155
+ propertyPath: 'Properties.EnableDnsHostnames',
156
+ expectedValue: true,
157
+ actualValue: false,
158
+ mutability: PropertyMutability.MUTABLE,
159
+ }),
160
+ ];
161
+
162
+ // Mock all mismatches can be reconciled
163
+ mockPropertyReconciler.canReconcile.mockResolvedValue(true);
164
+
165
+ // Mock batch reconciliation
166
+ mockPropertyReconciler.reconcileMultipleProperties.mockResolvedValue({
167
+ reconciledCount: 2,
168
+ failedCount: 0,
169
+ results: [
170
+ {
171
+ success: true,
172
+ mode: 'template',
173
+ propertyPath: 'Properties.EnableDnsSupport',
174
+ oldValue: true,
175
+ newValue: false,
176
+ message: 'Template updated',
177
+ },
178
+ {
179
+ success: true,
180
+ mode: 'template',
181
+ propertyPath: 'Properties.EnableDnsHostnames',
182
+ oldValue: true,
183
+ newValue: false,
184
+ message: 'Template updated',
185
+ },
186
+ ],
187
+ message: 'Reconciled 2 of 2 properties',
188
+ });
189
+
190
+ const result = await useCase.reconcileMultipleProperties({
191
+ stackIdentifier,
192
+ logicalId: 'MyVPC',
193
+ mismatches,
194
+ mode: 'template',
195
+ });
196
+
197
+ expect(result.reconciledCount).toBe(2);
198
+ expect(result.failedCount).toBe(0);
199
+ expect(result.results).toHaveLength(2);
200
+ });
201
+
202
+ it('should skip immutable properties and reconcile mutable ones', async () => {
203
+ const stackIdentifier = new StackIdentifier({
204
+ stackName: 'my-app-prod',
205
+ region: 'us-east-1',
206
+ });
207
+
208
+ const mismatches = [
209
+ new PropertyMismatch({
210
+ propertyPath: 'Properties.EnableDnsSupport',
211
+ expectedValue: true,
212
+ actualValue: false,
213
+ mutability: PropertyMutability.MUTABLE,
214
+ }),
215
+ new PropertyMismatch({
216
+ propertyPath: 'Properties.CidrBlock',
217
+ expectedValue: '10.0.0.0/16',
218
+ actualValue: '10.1.0.0/16',
219
+ mutability: PropertyMutability.IMMUTABLE,
220
+ }),
221
+ ];
222
+
223
+ // Mock first can be reconciled, second cannot
224
+ mockPropertyReconciler.canReconcile
225
+ .mockResolvedValueOnce(true)
226
+ .mockResolvedValueOnce(false);
227
+
228
+ // Mock reconciliation of the mutable property
229
+ mockPropertyReconciler.reconcileMultipleProperties.mockResolvedValue({
230
+ reconciledCount: 1,
231
+ failedCount: 0,
232
+ results: [
233
+ {
234
+ success: true,
235
+ mode: 'template',
236
+ propertyPath: 'Properties.EnableDnsSupport',
237
+ oldValue: true,
238
+ newValue: false,
239
+ message: 'Template updated',
240
+ },
241
+ ],
242
+ message: 'Reconciled 1 of 1 properties',
243
+ });
244
+
245
+ const result = await useCase.reconcileMultipleProperties({
246
+ stackIdentifier,
247
+ logicalId: 'MyVPC',
248
+ mismatches,
249
+ mode: 'template',
250
+ });
251
+
252
+ expect(result.reconciledCount).toBe(1); // One mutable property reconciled
253
+ expect(result.skippedCount).toBe(1); // One immutable property skipped
254
+ expect(result.reconcilableCount).toBe(1);
255
+ expect(result.results).toHaveLength(1);
256
+ });
257
+ });
258
+
259
+ describe('previewReconciliation', () => {
260
+ it('should preview reconciliation changes', async () => {
261
+ const stackIdentifier = new StackIdentifier({
262
+ stackName: 'my-app-prod',
263
+ region: 'us-east-1',
264
+ });
265
+
266
+ const mismatch = new PropertyMismatch({
267
+ propertyPath: 'Properties.EnableDnsSupport',
268
+ expectedValue: true,
269
+ actualValue: false,
270
+ mutability: PropertyMutability.MUTABLE,
271
+ });
272
+
273
+ // Mock preview
274
+ mockPropertyReconciler.previewReconciliation.mockResolvedValue({
275
+ canReconcile: true,
276
+ mode: 'template',
277
+ propertyPath: 'Properties.EnableDnsSupport',
278
+ currentValue: true,
279
+ proposedValue: false,
280
+ impact: 'Will update CloudFormation template to match actual resource state',
281
+ warnings: [],
282
+ });
283
+
284
+ const preview = await useCase.previewReconciliation({
285
+ stackIdentifier,
286
+ logicalId: 'MyVPC',
287
+ mismatch,
288
+ mode: 'template',
289
+ });
290
+
291
+ expect(preview.canReconcile).toBe(true);
292
+ expect(preview.mode).toBe('template');
293
+ expect(preview.impact).toContain('CloudFormation template');
294
+ });
295
+
296
+ it('should include warnings for risky reconciliations', async () => {
297
+ const stackIdentifier = new StackIdentifier({
298
+ stackName: 'my-app-prod',
299
+ region: 'us-east-1',
300
+ });
301
+
302
+ const mismatch = new PropertyMismatch({
303
+ propertyPath: 'Properties.EngineVersion',
304
+ expectedValue: '13.7',
305
+ actualValue: '13.8',
306
+ mutability: PropertyMutability.CONDITIONAL,
307
+ });
308
+
309
+ // Mock preview with warnings
310
+ mockPropertyReconciler.previewReconciliation.mockResolvedValue({
311
+ canReconcile: true,
312
+ mode: 'resource',
313
+ propertyPath: 'Properties.EngineVersion',
314
+ currentValue: '13.7',
315
+ proposedValue: '13.8',
316
+ impact: 'Will update database engine version - may cause downtime',
317
+ warnings: [
318
+ 'Engine version downgrade may not be supported',
319
+ 'Downtime may occur during version change',
320
+ ],
321
+ });
322
+
323
+ const preview = await useCase.previewReconciliation({
324
+ stackIdentifier,
325
+ logicalId: 'MyDBCluster',
326
+ mismatch,
327
+ mode: 'resource',
328
+ });
329
+
330
+ expect(preview.canReconcile).toBe(true);
331
+ expect(preview.warnings).toHaveLength(2);
332
+ expect(preview.warnings[0]).toContain('Engine version');
333
+ });
334
+ });
335
+
336
+ describe('constructor', () => {
337
+ it('should require propertyReconciler', () => {
338
+ expect(() => {
339
+ new ReconcilePropertiesUseCase({});
340
+ }).toThrow('propertyReconciler is required');
341
+ });
342
+ });
343
+ });