@friggframework/devtools 2.0.0-next.44 → 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 -2074
  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,275 @@
1
+ /**
2
+ * Tests for PropertyMismatch Entity
3
+ */
4
+
5
+ const PropertyMismatch = require('./property-mismatch');
6
+ const PropertyMutability = require('../value-objects/property-mutability');
7
+
8
+ describe('PropertyMismatch', () => {
9
+ describe('constructor', () => {
10
+ it('should create property mismatch with all fields', () => {
11
+ const mismatch = new PropertyMismatch({
12
+ propertyPath: 'Properties.BucketName',
13
+ expectedValue: 'my-app-prod-bucket',
14
+ actualValue: 'my-app-dev-bucket',
15
+ mutability: PropertyMutability.IMMUTABLE,
16
+ });
17
+
18
+ expect(mismatch.propertyPath).toBe('Properties.BucketName');
19
+ expect(mismatch.expectedValue).toBe('my-app-prod-bucket');
20
+ expect(mismatch.actualValue).toBe('my-app-dev-bucket');
21
+ expect(mismatch.mutability.value).toBe('IMMUTABLE');
22
+ });
23
+
24
+ it('should require propertyPath', () => {
25
+ expect(() => {
26
+ new PropertyMismatch({
27
+ expectedValue: 'value1',
28
+ actualValue: 'value2',
29
+ mutability: PropertyMutability.MUTABLE,
30
+ });
31
+ }).toThrow('propertyPath is required');
32
+ });
33
+
34
+ it('should require expectedValue parameter', () => {
35
+ expect(() => {
36
+ new PropertyMismatch({
37
+ propertyPath: 'Properties.Name',
38
+ actualValue: 'value2',
39
+ mutability: PropertyMutability.MUTABLE,
40
+ });
41
+ }).toThrow('expectedValue must be provided');
42
+ });
43
+
44
+ it('should require actualValue parameter', () => {
45
+ expect(() => {
46
+ new PropertyMismatch({
47
+ propertyPath: 'Properties.Name',
48
+ expectedValue: 'value1',
49
+ mutability: PropertyMutability.MUTABLE,
50
+ });
51
+ }).toThrow('actualValue must be provided');
52
+ });
53
+
54
+ it('should accept undefined as expectedValue', () => {
55
+ const mismatch = new PropertyMismatch({
56
+ propertyPath: 'Properties.NewProperty',
57
+ expectedValue: undefined,
58
+ actualValue: 'new-value',
59
+ mutability: PropertyMutability.MUTABLE,
60
+ });
61
+
62
+ expect(mismatch.expectedValue).toBeUndefined();
63
+ expect(mismatch.actualValue).toBe('new-value');
64
+ });
65
+
66
+ it('should accept undefined as actualValue', () => {
67
+ const mismatch = new PropertyMismatch({
68
+ propertyPath: 'Properties.OldProperty',
69
+ expectedValue: 'old-value',
70
+ actualValue: undefined,
71
+ mutability: PropertyMutability.MUTABLE,
72
+ });
73
+
74
+ expect(mismatch.expectedValue).toBe('old-value');
75
+ expect(mismatch.actualValue).toBeUndefined();
76
+ });
77
+
78
+ it('should require mutability', () => {
79
+ expect(() => {
80
+ new PropertyMismatch({
81
+ propertyPath: 'Properties.Name',
82
+ expectedValue: 'value1',
83
+ actualValue: 'value2',
84
+ });
85
+ }).toThrow('mutability is required');
86
+ });
87
+
88
+ it('should accept null as expectedValue', () => {
89
+ const mismatch = new PropertyMismatch({
90
+ propertyPath: 'Properties.Tags',
91
+ expectedValue: null,
92
+ actualValue: ['tag1', 'tag2'],
93
+ mutability: PropertyMutability.MUTABLE,
94
+ });
95
+
96
+ expect(mismatch.expectedValue).toBeNull();
97
+ });
98
+
99
+ it('should accept null as actualValue', () => {
100
+ const mismatch = new PropertyMismatch({
101
+ propertyPath: 'Properties.Tags',
102
+ expectedValue: ['tag1', 'tag2'],
103
+ actualValue: null,
104
+ mutability: PropertyMutability.MUTABLE,
105
+ });
106
+
107
+ expect(mismatch.actualValue).toBeNull();
108
+ });
109
+ });
110
+
111
+ describe('requiresReplacement', () => {
112
+ it('should return true for immutable property', () => {
113
+ const mismatch = new PropertyMismatch({
114
+ propertyPath: 'Properties.BucketName',
115
+ expectedValue: 'bucket1',
116
+ actualValue: 'bucket2',
117
+ mutability: PropertyMutability.IMMUTABLE,
118
+ });
119
+
120
+ expect(mismatch.requiresReplacement()).toBe(true);
121
+ });
122
+
123
+ it('should return false for mutable property', () => {
124
+ const mismatch = new PropertyMismatch({
125
+ propertyPath: 'Properties.Tags',
126
+ expectedValue: ['tag1'],
127
+ actualValue: ['tag2'],
128
+ mutability: PropertyMutability.MUTABLE,
129
+ });
130
+
131
+ expect(mismatch.requiresReplacement()).toBe(false);
132
+ });
133
+
134
+ it('should return false for conditional property', () => {
135
+ const mismatch = new PropertyMismatch({
136
+ propertyPath: 'Properties.EngineVersion',
137
+ expectedValue: '5.7',
138
+ actualValue: '5.6',
139
+ mutability: PropertyMutability.CONDITIONAL,
140
+ });
141
+
142
+ expect(mismatch.requiresReplacement()).toBe(false);
143
+ });
144
+ });
145
+
146
+ describe('canAutoFix', () => {
147
+ it('should return true for mutable property', () => {
148
+ const mismatch = new PropertyMismatch({
149
+ propertyPath: 'Properties.Tags',
150
+ expectedValue: ['tag1'],
151
+ actualValue: ['tag2'],
152
+ mutability: PropertyMutability.MUTABLE,
153
+ });
154
+
155
+ expect(mismatch.canAutoFix()).toBe(true);
156
+ });
157
+
158
+ it('should return false for immutable property', () => {
159
+ const mismatch = new PropertyMismatch({
160
+ propertyPath: 'Properties.BucketName',
161
+ expectedValue: 'bucket1',
162
+ actualValue: 'bucket2',
163
+ mutability: PropertyMutability.IMMUTABLE,
164
+ });
165
+
166
+ expect(mismatch.canAutoFix()).toBe(false);
167
+ });
168
+
169
+ it('should return false for conditional property', () => {
170
+ const mismatch = new PropertyMismatch({
171
+ propertyPath: 'Properties.EngineVersion',
172
+ expectedValue: '5.7',
173
+ actualValue: '5.6',
174
+ mutability: PropertyMutability.CONDITIONAL,
175
+ });
176
+
177
+ expect(mismatch.canAutoFix()).toBe(false);
178
+ });
179
+ });
180
+
181
+ describe('getSeverity', () => {
182
+ it('should return critical for immutable property mismatch', () => {
183
+ const mismatch = new PropertyMismatch({
184
+ propertyPath: 'Properties.BucketName',
185
+ expectedValue: 'bucket1',
186
+ actualValue: 'bucket2',
187
+ mutability: PropertyMutability.IMMUTABLE,
188
+ });
189
+
190
+ expect(mismatch.getSeverity()).toBe('critical');
191
+ });
192
+
193
+ it('should return warning for mutable property mismatch', () => {
194
+ const mismatch = new PropertyMismatch({
195
+ propertyPath: 'Properties.Tags',
196
+ expectedValue: ['tag1'],
197
+ actualValue: ['tag2'],
198
+ mutability: PropertyMutability.MUTABLE,
199
+ });
200
+
201
+ expect(mismatch.getSeverity()).toBe('warning');
202
+ });
203
+
204
+ it('should return warning for conditional property mismatch', () => {
205
+ const mismatch = new PropertyMismatch({
206
+ propertyPath: 'Properties.EngineVersion',
207
+ expectedValue: '5.7',
208
+ actualValue: '5.6',
209
+ mutability: PropertyMutability.CONDITIONAL,
210
+ });
211
+
212
+ expect(mismatch.getSeverity()).toBe('warning');
213
+ });
214
+ });
215
+
216
+ describe('toString', () => {
217
+ it('should return string representation', () => {
218
+ const mismatch = new PropertyMismatch({
219
+ propertyPath: 'Properties.BucketName',
220
+ expectedValue: 'bucket1',
221
+ actualValue: 'bucket2',
222
+ mutability: PropertyMutability.IMMUTABLE,
223
+ });
224
+
225
+ expect(mismatch.toString()).toBe(
226
+ 'PropertyMismatch: Properties.BucketName (expected: bucket1, actual: bucket2, mutability: IMMUTABLE)'
227
+ );
228
+ });
229
+
230
+ it('should handle null expected value', () => {
231
+ const mismatch = new PropertyMismatch({
232
+ propertyPath: 'Properties.Tags',
233
+ expectedValue: null,
234
+ actualValue: ['tag1'],
235
+ mutability: PropertyMutability.MUTABLE,
236
+ });
237
+
238
+ expect(mismatch.toString()).toContain('expected: null');
239
+ });
240
+
241
+ it('should handle null actual value', () => {
242
+ const mismatch = new PropertyMismatch({
243
+ propertyPath: 'Properties.Tags',
244
+ expectedValue: ['tag1'],
245
+ actualValue: null,
246
+ mutability: PropertyMutability.MUTABLE,
247
+ });
248
+
249
+ expect(mismatch.toString()).toContain('actual: null');
250
+ });
251
+ });
252
+
253
+ describe('toJSON', () => {
254
+ it('should serialize to JSON', () => {
255
+ const mismatch = new PropertyMismatch({
256
+ propertyPath: 'Properties.BucketName',
257
+ expectedValue: 'bucket1',
258
+ actualValue: 'bucket2',
259
+ mutability: PropertyMutability.IMMUTABLE,
260
+ });
261
+
262
+ const json = mismatch.toJSON();
263
+
264
+ expect(json).toEqual({
265
+ propertyPath: 'Properties.BucketName',
266
+ expectedValue: 'bucket1',
267
+ actualValue: 'bucket2',
268
+ mutability: 'IMMUTABLE',
269
+ severity: 'critical',
270
+ canAutoFix: false,
271
+ requiresReplacement: true,
272
+ });
273
+ });
274
+ });
275
+ });
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Resource Entity
3
+ *
4
+ * Represents a CloudFormation resource with its current state and any detected issues
5
+ */
6
+
7
+ const ResourceState = require('../value-objects/resource-state');
8
+
9
+ class Resource {
10
+ /**
11
+ * Create a new Resource
12
+ *
13
+ * @param {Object} params
14
+ * @param {string|null} params.logicalId - CloudFormation logical ID (null for orphaned resources)
15
+ * @param {string} params.physicalId - Physical resource ID in cloud provider
16
+ * @param {string} params.resourceType - CloudFormation resource type (e.g., AWS::EC2::VPC)
17
+ * @param {ResourceState} params.state - Resource state
18
+ * @param {Object} [params.properties={}] - Resource properties
19
+ * @param {Issue[]} [params.issues=[]] - Detected issues with this resource
20
+ */
21
+ constructor({
22
+ logicalId = null,
23
+ physicalId,
24
+ resourceType,
25
+ state,
26
+ properties = {},
27
+ issues = [],
28
+ }) {
29
+ // Validate required fields
30
+ if (physicalId === undefined || physicalId === null) {
31
+ throw new Error('physicalId is required');
32
+ }
33
+
34
+ if (!resourceType) {
35
+ throw new Error('resourceType is required');
36
+ }
37
+
38
+ if (!state) {
39
+ throw new Error('state is required');
40
+ }
41
+
42
+ if (!(state instanceof ResourceState)) {
43
+ throw new Error('state must be a ResourceState instance');
44
+ }
45
+
46
+ this.logicalId = logicalId;
47
+ this.physicalId = physicalId;
48
+ this.resourceType = resourceType;
49
+ this.state = state;
50
+ this.properties = properties;
51
+ this.issues = [...issues]; // Copy array to avoid mutations
52
+ }
53
+
54
+ /**
55
+ * Check if resource is in CloudFormation stack
56
+ * @returns {boolean}
57
+ */
58
+ isInStack() {
59
+ return this.state.isInStack();
60
+ }
61
+
62
+ /**
63
+ * Check if resource is orphaned (exists in cloud but not in stack)
64
+ * @returns {boolean}
65
+ */
66
+ isOrphaned() {
67
+ return this.state.isOrphaned();
68
+ }
69
+
70
+ /**
71
+ * Check if resource is missing (exists in stack but not in cloud)
72
+ * @returns {boolean}
73
+ */
74
+ isMissing() {
75
+ return this.state.isMissing();
76
+ }
77
+
78
+ /**
79
+ * Check if resource has drifted (properties differ)
80
+ * @returns {boolean}
81
+ */
82
+ isDrifted() {
83
+ return this.state.isDrifted();
84
+ }
85
+
86
+ /**
87
+ * Add an issue to this resource
88
+ * @param {Issue} issue
89
+ */
90
+ addIssue(issue) {
91
+ this.issues.push(issue);
92
+ }
93
+
94
+ /**
95
+ * Check if resource has any issues
96
+ * @returns {boolean}
97
+ */
98
+ hasIssues() {
99
+ return this.issues.length > 0;
100
+ }
101
+
102
+ /**
103
+ * Check if resource has critical issues
104
+ * @returns {boolean}
105
+ */
106
+ hasCriticalIssues() {
107
+ return this.issues.some(issue => issue.isCritical());
108
+ }
109
+
110
+ /**
111
+ * Get all critical issues
112
+ * @returns {Issue[]}
113
+ */
114
+ getCriticalIssues() {
115
+ return this.issues.filter(issue => issue.isCritical());
116
+ }
117
+
118
+ /**
119
+ * Check if resource is healthy (no issues)
120
+ * @returns {boolean}
121
+ */
122
+ isHealthy() {
123
+ return !this.hasIssues();
124
+ }
125
+
126
+ /**
127
+ * Get resource identifier (logical ID or physical ID)
128
+ * @returns {string}
129
+ */
130
+ getIdentifier() {
131
+ return this.logicalId || this.physicalId;
132
+ }
133
+
134
+ /**
135
+ * Get string representation
136
+ * @returns {string}
137
+ */
138
+ toString() {
139
+ return `Resource: ${this.resourceType} [${this.state.toString()}] - LogicalId: ${this.logicalId}, PhysicalId: ${this.physicalId}`;
140
+ }
141
+
142
+ /**
143
+ * Serialize to JSON
144
+ * @returns {Object}
145
+ */
146
+ toJSON() {
147
+ return {
148
+ logicalId: this.logicalId,
149
+ physicalId: this.physicalId,
150
+ resourceType: this.resourceType,
151
+ state: this.state.toString(),
152
+ properties: this.properties,
153
+ issues: this.issues.map(issue => issue.toJSON()),
154
+ isHealthy: this.isHealthy(),
155
+ };
156
+ }
157
+ }
158
+
159
+ module.exports = Resource;