@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,552 @@
1
+ /**
2
+ * Tests for Resource Discovery Service
3
+ *
4
+ * Tests orchestration of cloud resource discovery
5
+ */
6
+
7
+ const { shouldRunDiscovery, gatherDiscoveredResources } = require('./resource-discovery');
8
+
9
+ // Mock the provider factory and discovery classes
10
+ jest.mock('./providers/provider-factory');
11
+ jest.mock('../networking/vpc-discovery');
12
+ jest.mock('../security/kms-discovery');
13
+ jest.mock('../database/aurora-discovery');
14
+ jest.mock('../parameters/ssm-discovery');
15
+
16
+ const { CloudProviderFactory } = require('./providers/provider-factory');
17
+ const { VpcDiscovery } = require('../networking/vpc-discovery');
18
+ const { KmsDiscovery } = require('../security/kms-discovery');
19
+ const { AuroraDiscovery } = require('../database/aurora-discovery');
20
+ const { SsmDiscovery } = require('../parameters/ssm-discovery');
21
+
22
+ describe('Resource Discovery', () => {
23
+ let mockProvider;
24
+ let mockVpcDiscovery;
25
+ let mockKmsDiscovery;
26
+ let mockAuroraDiscovery;
27
+ let mockSsmDiscovery;
28
+
29
+ beforeEach(() => {
30
+ // Reset environment
31
+ delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
32
+ delete process.env.CLOUD_PROVIDER;
33
+ delete process.env.AWS_REGION;
34
+
35
+ // Create mock provider
36
+ mockProvider = {
37
+ getName: jest.fn().mockReturnValue('aws'),
38
+ };
39
+
40
+ // Create mock discoveries with default responses
41
+ mockVpcDiscovery = {
42
+ discover: jest.fn().mockResolvedValue({
43
+ defaultVpcId: 'vpc-123',
44
+ privateSubnetId1: 'subnet-1',
45
+ }),
46
+ };
47
+
48
+ mockKmsDiscovery = {
49
+ discover: jest.fn().mockResolvedValue({
50
+ kmsKeyId: 'arn:aws:kms:us-east-1:123:key/abc',
51
+ }),
52
+ };
53
+
54
+ mockAuroraDiscovery = {
55
+ discover: jest.fn().mockResolvedValue({
56
+ auroraClusterEndpoint: 'db.example.com',
57
+ }),
58
+ };
59
+
60
+ mockSsmDiscovery = {
61
+ discover: jest.fn().mockResolvedValue({
62
+ parameters: [],
63
+ }),
64
+ };
65
+
66
+ // Mock factory and discovery constructors
67
+ CloudProviderFactory.create = jest.fn().mockReturnValue(mockProvider);
68
+ VpcDiscovery.mockImplementation(() => mockVpcDiscovery);
69
+ KmsDiscovery.mockImplementation(() => mockKmsDiscovery);
70
+ AuroraDiscovery.mockImplementation(() => mockAuroraDiscovery);
71
+ SsmDiscovery.mockImplementation(() => mockSsmDiscovery);
72
+ });
73
+
74
+ describe('shouldRunDiscovery()', () => {
75
+ it('should return false when FRIGG_SKIP_AWS_DISCOVERY is true', () => {
76
+ process.env.FRIGG_SKIP_AWS_DISCOVERY = 'true';
77
+
78
+ const result = shouldRunDiscovery({ vpc: { enable: true } });
79
+
80
+ expect(result).toBe(false);
81
+ });
82
+
83
+ it('should return true when VPC is enabled', () => {
84
+ const result = shouldRunDiscovery({ vpc: { enable: true } });
85
+
86
+ expect(result).toBe(true);
87
+ });
88
+
89
+ it('should return true when KMS encryption is enabled', () => {
90
+ const result = shouldRunDiscovery({
91
+ encryption: { fieldLevelEncryptionMethod: 'kms' },
92
+ });
93
+
94
+ expect(result).toBe(true);
95
+ });
96
+
97
+ it('should return true when SSM is enabled', () => {
98
+ const result = shouldRunDiscovery({ ssm: { enable: true } });
99
+
100
+ expect(result).toBe(true);
101
+ });
102
+
103
+ it('should return true when Postgres database is enabled', () => {
104
+ const result = shouldRunDiscovery({
105
+ database: { postgres: { enable: true } },
106
+ });
107
+
108
+ expect(result).toBe(true);
109
+ });
110
+
111
+ it('should return false when no features require discovery', () => {
112
+ const result = shouldRunDiscovery({
113
+ vpc: { enable: false },
114
+ encryption: { fieldLevelEncryptionMethod: 'aes' },
115
+ ssm: { enable: false },
116
+ });
117
+
118
+ expect(result).toBe(false);
119
+ });
120
+
121
+ it('should return false for empty app definition', () => {
122
+ const result = shouldRunDiscovery({});
123
+
124
+ expect(result).toBe(false);
125
+ });
126
+ });
127
+
128
+ describe('gatherDiscoveredResources()', () => {
129
+ it('should skip discovery when not needed', async () => {
130
+ const appDefinition = {
131
+ vpc: { enable: false },
132
+ };
133
+
134
+ const result = await gatherDiscoveredResources(appDefinition);
135
+
136
+ expect(result).toEqual({});
137
+ expect(CloudProviderFactory.create).not.toHaveBeenCalled();
138
+ });
139
+
140
+ it('should create AWS provider by default', async () => {
141
+ const appDefinition = {
142
+ vpc: { enable: true },
143
+ };
144
+
145
+ await gatherDiscoveredResources(appDefinition);
146
+
147
+ expect(CloudProviderFactory.create).toHaveBeenCalledWith('aws', 'us-east-1');
148
+ });
149
+
150
+ it('should respect CLOUD_PROVIDER environment variable', async () => {
151
+ process.env.CLOUD_PROVIDER = 'gcp';
152
+ process.env.AWS_REGION = 'us-central1';
153
+
154
+ const appDefinition = {
155
+ vpc: { enable: true },
156
+ };
157
+
158
+ await gatherDiscoveredResources(appDefinition);
159
+
160
+ expect(CloudProviderFactory.create).toHaveBeenCalledWith('gcp', 'us-central1');
161
+ });
162
+
163
+ it('should respect AWS_REGION environment variable', async () => {
164
+ process.env.AWS_REGION = 'eu-west-1';
165
+
166
+ const appDefinition = {
167
+ vpc: { enable: true },
168
+ };
169
+
170
+ await gatherDiscoveredResources(appDefinition);
171
+
172
+ expect(CloudProviderFactory.create).toHaveBeenCalledWith('aws', 'eu-west-1');
173
+ });
174
+
175
+ it('should create discovery services with provider', async () => {
176
+ const appDefinition = {
177
+ vpc: { enable: true },
178
+ };
179
+
180
+ await gatherDiscoveredResources(appDefinition);
181
+
182
+ expect(VpcDiscovery).toHaveBeenCalledWith(mockProvider);
183
+ expect(KmsDiscovery).toHaveBeenCalledWith(mockProvider);
184
+ expect(AuroraDiscovery).toHaveBeenCalledWith(mockProvider);
185
+ expect(SsmDiscovery).toHaveBeenCalledWith(mockProvider);
186
+ });
187
+
188
+ it('should run VPC discovery when enabled', async () => {
189
+ const appDefinition = {
190
+ name: 'test-app',
191
+ vpc: { enable: true },
192
+ };
193
+
194
+ await gatherDiscoveredResources(appDefinition);
195
+
196
+ expect(mockVpcDiscovery.discover).toHaveBeenCalledWith(
197
+ expect.objectContaining({
198
+ serviceName: 'test-app',
199
+ })
200
+ );
201
+ });
202
+
203
+ it('should skip VPC discovery when disabled', async () => {
204
+ const appDefinition = {
205
+ vpc: { enable: false },
206
+ encryption: { fieldLevelEncryptionMethod: 'kms' },
207
+ };
208
+
209
+ await gatherDiscoveredResources(appDefinition);
210
+
211
+ expect(mockVpcDiscovery.discover).not.toHaveBeenCalled();
212
+ });
213
+
214
+ it('should run KMS discovery when encryption is kms', async () => {
215
+ const appDefinition = {
216
+ name: 'test-app',
217
+ encryption: { fieldLevelEncryptionMethod: 'kms' },
218
+ };
219
+
220
+ await gatherDiscoveredResources(appDefinition);
221
+
222
+ expect(mockKmsDiscovery.discover).toHaveBeenCalled();
223
+ });
224
+
225
+ it('should skip KMS discovery when encryption is not kms', async () => {
226
+ const appDefinition = {
227
+ encryption: { fieldLevelEncryptionMethod: 'aes' },
228
+ vpc: { enable: true },
229
+ };
230
+
231
+ await gatherDiscoveredResources(appDefinition);
232
+
233
+ expect(mockKmsDiscovery.discover).not.toHaveBeenCalled();
234
+ });
235
+
236
+ it('should run database discovery when postgres is enabled', async () => {
237
+ const appDefinition = {
238
+ database: { postgres: { enable: true } },
239
+ };
240
+
241
+ await gatherDiscoveredResources(appDefinition);
242
+
243
+ expect(mockAuroraDiscovery.discover).toHaveBeenCalled();
244
+ });
245
+
246
+ it('should skip database discovery when postgres is disabled', async () => {
247
+ const appDefinition = {
248
+ database: { postgres: { enable: false } },
249
+ vpc: { enable: true },
250
+ };
251
+
252
+ await gatherDiscoveredResources(appDefinition);
253
+
254
+ expect(mockAuroraDiscovery.discover).not.toHaveBeenCalled();
255
+ });
256
+
257
+ it('should run SSM discovery when enabled', async () => {
258
+ const appDefinition = {
259
+ ssm: { enable: true },
260
+ };
261
+
262
+ await gatherDiscoveredResources(appDefinition);
263
+
264
+ expect(mockSsmDiscovery.discover).toHaveBeenCalled();
265
+ });
266
+
267
+ it('should aggregate results from all discoveries', async () => {
268
+ mockVpcDiscovery.discover.mockResolvedValue({
269
+ defaultVpcId: 'vpc-123',
270
+ privateSubnetId1: 'subnet-1',
271
+ });
272
+
273
+ mockKmsDiscovery.discover.mockResolvedValue({
274
+ kmsKeyId: 'arn:aws:kms:key/abc',
275
+ });
276
+
277
+ mockAuroraDiscovery.discover.mockResolvedValue({
278
+ auroraClusterEndpoint: 'db.example.com',
279
+ auroraPort: 5432,
280
+ });
281
+
282
+ mockSsmDiscovery.discover.mockResolvedValue({
283
+ parameters: ['param1'],
284
+ });
285
+
286
+ const appDefinition = {
287
+ vpc: { enable: true },
288
+ encryption: { fieldLevelEncryptionMethod: 'kms' },
289
+ database: { postgres: { enable: true } },
290
+ ssm: { enable: true },
291
+ };
292
+
293
+ const result = await gatherDiscoveredResources(appDefinition);
294
+
295
+ expect(result).toEqual({
296
+ defaultVpcId: 'vpc-123',
297
+ privateSubnetId1: 'subnet-1',
298
+ kmsKeyId: 'arn:aws:kms:key/abc',
299
+ auroraClusterEndpoint: 'db.example.com',
300
+ auroraPort: 5432,
301
+ parameters: ['param1'],
302
+ });
303
+ });
304
+
305
+ it('should run discoveries in parallel for performance', async () => {
306
+ const appDefinition = {
307
+ vpc: { enable: true },
308
+ encryption: { fieldLevelEncryptionMethod: 'kms' },
309
+ database: { postgres: { enable: true } },
310
+ ssm: { enable: true },
311
+ };
312
+
313
+ await gatherDiscoveredResources(appDefinition);
314
+
315
+ // All should have been called (proving parallel execution)
316
+ expect(mockVpcDiscovery.discover).toHaveBeenCalled();
317
+ expect(mockKmsDiscovery.discover).toHaveBeenCalled();
318
+ expect(mockAuroraDiscovery.discover).toHaveBeenCalled();
319
+ expect(mockSsmDiscovery.discover).toHaveBeenCalled();
320
+ });
321
+
322
+ it('should handle discovery errors gracefully', async () => {
323
+ mockVpcDiscovery.discover.mockRejectedValue(new Error('VPC API Error'));
324
+
325
+ const appDefinition = {
326
+ vpc: { enable: true },
327
+ };
328
+
329
+ const result = await gatherDiscoveredResources(appDefinition);
330
+
331
+ // Should return empty object instead of throwing
332
+ expect(result).toEqual({});
333
+ });
334
+
335
+ it('should pass configuration to discoveries', async () => {
336
+ const appDefinition = {
337
+ name: 'my-service',
338
+ vpc: {
339
+ enable: true,
340
+ vpcId: 'vpc-custom',
341
+ },
342
+ database: {
343
+ postgres: {
344
+ enable: true,
345
+ clusterId: 'my-cluster',
346
+ },
347
+ },
348
+ encryption: {
349
+ fieldLevelEncryptionMethod: 'kms',
350
+ keyAlias: 'alias/my-key',
351
+ },
352
+ };
353
+
354
+ process.env.SLS_STAGE = 'production';
355
+
356
+ await gatherDiscoveredResources(appDefinition);
357
+
358
+ expect(mockVpcDiscovery.discover).toHaveBeenCalledWith(
359
+ expect.objectContaining({
360
+ serviceName: 'my-service',
361
+ stage: 'production',
362
+ vpcId: 'vpc-custom',
363
+ })
364
+ );
365
+
366
+ expect(mockAuroraDiscovery.discover).toHaveBeenCalledWith(
367
+ expect.objectContaining({
368
+ databaseId: 'my-cluster',
369
+ })
370
+ );
371
+
372
+ expect(mockKmsDiscovery.discover).toHaveBeenCalledWith(
373
+ expect.objectContaining({
374
+ keyAlias: 'alias/my-key',
375
+ })
376
+ );
377
+ });
378
+
379
+ it('should default stage to dev', async () => {
380
+ delete process.env.SLS_STAGE;
381
+
382
+ const appDefinition = {
383
+ vpc: { enable: true },
384
+ };
385
+
386
+ await gatherDiscoveredResources(appDefinition);
387
+
388
+ expect(mockVpcDiscovery.discover).toHaveBeenCalledWith(
389
+ expect.objectContaining({
390
+ stage: 'dev',
391
+ })
392
+ );
393
+ });
394
+
395
+ it('should read stage from SLS_STAGE environment variable for CLI integration', async () => {
396
+ // This test documents the contract between frigg CLI and discovery
397
+ // The CLI sets SLS_STAGE environment variable when user passes --stage flag
398
+ process.env.SLS_STAGE = 'qa';
399
+
400
+ const appDefinition = {
401
+ name: 'quo-integrations',
402
+ vpc: { enable: true },
403
+ };
404
+
405
+ await gatherDiscoveredResources(appDefinition);
406
+
407
+ // Verify stage is read from SLS_STAGE
408
+ expect(mockVpcDiscovery.discover).toHaveBeenCalledWith(
409
+ expect.objectContaining({
410
+ serviceName: 'quo-integrations',
411
+ stage: 'qa',
412
+ })
413
+ );
414
+
415
+ delete process.env.SLS_STAGE;
416
+ });
417
+
418
+ it('should include secrets in SSM discovery by default', async () => {
419
+ const appDefinition = {
420
+ ssm: { enable: true },
421
+ };
422
+
423
+ await gatherDiscoveredResources(appDefinition);
424
+
425
+ expect(mockSsmDiscovery.discover).toHaveBeenCalledWith(
426
+ expect.objectContaining({
427
+ includeSecrets: true,
428
+ })
429
+ );
430
+ });
431
+ });
432
+
433
+ describe('Isolated Mode Discovery', () => {
434
+ beforeEach(() => {
435
+ // Mock CloudFormation discovery
436
+ jest.mock('./cloudformation-discovery');
437
+ const { CloudFormationDiscovery } = require('./cloudformation-discovery');
438
+ CloudFormationDiscovery.mockImplementation(() => ({
439
+ discoverFromStack: jest.fn().mockResolvedValue({}), // No stack found
440
+ }));
441
+ });
442
+
443
+ it('should discover KMS but not VPC/Aurora in isolated mode', async () => {
444
+ const appDefinition = {
445
+ name: 'test-app',
446
+ managementMode: 'managed',
447
+ vpcIsolation: 'isolated',
448
+ vpc: { enable: true },
449
+ database: { postgres: { enable: true } },
450
+ };
451
+
452
+ process.env.SLS_STAGE = 'dev';
453
+
454
+ // Mock KMS discovery returning a shared key
455
+ mockKmsDiscovery.discover.mockResolvedValue({
456
+ defaultKmsKeyId: 'shared-kms-key-123',
457
+ });
458
+
459
+ const result = await gatherDiscoveredResources(appDefinition);
460
+
461
+ // Should return KMS (shareable) but not VPC/Aurora (isolated)
462
+ expect(result).toEqual({
463
+ defaultKmsKeyId: 'shared-kms-key-123',
464
+ });
465
+
466
+ // Should call KMS discovery (shared) but NOT VPC/Aurora discovery (isolated)
467
+ expect(mockKmsDiscovery.discover).toHaveBeenCalled();
468
+ expect(mockVpcDiscovery.discover).not.toHaveBeenCalled();
469
+ expect(mockAuroraDiscovery.discover).not.toHaveBeenCalled();
470
+ });
471
+
472
+ it('should return empty if no KMS found in isolated mode (fresh infrastructure)', async () => {
473
+ const { CloudFormationDiscovery } = require('./cloudformation-discovery');
474
+
475
+ // Mock that CF stack exists but we still want fresh resources
476
+ CloudFormationDiscovery.mockImplementation(() => ({
477
+ discoverFromStack: jest.fn().mockResolvedValue({}), // Stack exists but empty
478
+ }));
479
+
480
+ const appDefinition = {
481
+ name: 'test-app',
482
+ managementMode: 'managed',
483
+ vpcIsolation: 'isolated',
484
+ vpc: { enable: true },
485
+ };
486
+
487
+ process.env.SLS_STAGE = 'dev';
488
+
489
+ // Mock KMS discovery finding nothing
490
+ mockKmsDiscovery.discover.mockResolvedValue({});
491
+
492
+ const result = await gatherDiscoveredResources(appDefinition);
493
+
494
+ // Should return empty (no VPC/Aurora, and KMS not found)
495
+ // This will trigger fresh KMS creation
496
+ expect(result).toEqual({});
497
+
498
+ // Should call KMS discovery but NOT VPC/Aurora
499
+ expect(mockKmsDiscovery.discover).toHaveBeenCalled();
500
+ expect(mockVpcDiscovery.discover).not.toHaveBeenCalled();
501
+ });
502
+
503
+ it('should use AWS API discovery in shared mode', async () => {
504
+ const appDefinition = {
505
+ name: 'test-app',
506
+ managementMode: 'managed',
507
+ vpcIsolation: 'shared', // NOT isolated
508
+ vpc: { enable: true },
509
+ };
510
+
511
+ await gatherDiscoveredResources(appDefinition);
512
+
513
+ // Should call AWS API discovery (shared mode finds resources across stages)
514
+ expect(mockVpcDiscovery.discover).toHaveBeenCalled();
515
+ });
516
+
517
+ it('should search for specific KMS alias in isolated mode to find orphaned keys', async () => {
518
+ const appDefinition = {
519
+ name: 'quo-integrations',
520
+ managementMode: 'managed',
521
+ vpcIsolation: 'isolated',
522
+ encryption: { fieldLevelEncryptionMethod: 'kms' },
523
+ };
524
+
525
+ process.env.SLS_STAGE = 'dev';
526
+
527
+ // Mock KMS discovery finding key via alias search
528
+ mockKmsDiscovery.discover.mockResolvedValue({
529
+ defaultKmsKeyId: 'arn:aws:kms:us-east-1:123:key/found-via-alias',
530
+ kmsKeyAlias: 'alias/quo-integrations-dev-frigg-kms',
531
+ });
532
+
533
+ const result = await gatherDiscoveredResources(appDefinition);
534
+
535
+ // Should pass keyAlias to discover orphaned KMS keys
536
+ expect(mockKmsDiscovery.discover).toHaveBeenCalledWith(
537
+ expect.objectContaining({
538
+ serviceName: 'quo-integrations',
539
+ stage: 'dev',
540
+ keyAlias: 'alias/quo-integrations-dev-frigg-kms',
541
+ })
542
+ );
543
+
544
+ // Should return discovered KMS key (even if orphaned from stack)
545
+ expect(result).toEqual({
546
+ defaultKmsKeyId: 'arn:aws:kms:us-east-1:123:key/found-via-alias',
547
+ kmsKeyAlias: 'alias/quo-integrations-dev-frigg-kms',
548
+ });
549
+ });
550
+ });
551
+ });
552
+