@friggframework/devtools 2.0.0-next.3 → 2.0.0-next.30

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 (199) hide show
  1. package/frigg-cli/.eslintrc.js +141 -0
  2. package/frigg-cli/__tests__/jest.config.js +102 -0
  3. package/frigg-cli/__tests__/unit/commands/build.test.js +483 -0
  4. package/frigg-cli/__tests__/unit/commands/install.test.js +418 -0
  5. package/frigg-cli/__tests__/unit/commands/ui.test.js +592 -0
  6. package/frigg-cli/__tests__/utils/command-tester.js +170 -0
  7. package/frigg-cli/__tests__/utils/mock-factory.js +270 -0
  8. package/frigg-cli/__tests__/utils/test-fixtures.js +463 -0
  9. package/frigg-cli/__tests__/utils/test-setup.js +286 -0
  10. package/frigg-cli/build-command/index.js +54 -0
  11. package/frigg-cli/deploy-command/index.js +36 -0
  12. package/frigg-cli/generate-command/__tests__/generate-command.test.js +312 -0
  13. package/frigg-cli/generate-command/azure-generator.js +43 -0
  14. package/frigg-cli/generate-command/gcp-generator.js +47 -0
  15. package/frigg-cli/generate-command/index.js +332 -0
  16. package/frigg-cli/generate-command/terraform-generator.js +555 -0
  17. package/frigg-cli/generate-iam-command.js +115 -0
  18. package/frigg-cli/index.js +47 -1
  19. package/frigg-cli/index.test.js +1 -4
  20. package/frigg-cli/init-command/backend-first-handler.js +756 -0
  21. package/frigg-cli/init-command/index.js +93 -0
  22. package/frigg-cli/init-command/template-handler.js +143 -0
  23. package/frigg-cli/install-command/index.js +1 -4
  24. package/frigg-cli/package.json +51 -0
  25. package/frigg-cli/start-command/index.js +24 -4
  26. package/frigg-cli/test/init-command.test.js +180 -0
  27. package/frigg-cli/test/npm-registry.test.js +319 -0
  28. package/frigg-cli/ui-command/index.js +154 -0
  29. package/frigg-cli/utils/app-resolver.js +319 -0
  30. package/frigg-cli/utils/backend-path.js +16 -17
  31. package/frigg-cli/utils/npm-registry.js +167 -0
  32. package/frigg-cli/utils/process-manager.js +199 -0
  33. package/frigg-cli/utils/repo-detection.js +405 -0
  34. package/infrastructure/AWS-DISCOVERY-TROUBLESHOOTING.md +245 -0
  35. package/infrastructure/AWS-IAM-CREDENTIAL-NEEDS.md +596 -0
  36. package/infrastructure/DEPLOYMENT-INSTRUCTIONS.md +268 -0
  37. package/infrastructure/GENERATE-IAM-DOCS.md +253 -0
  38. package/infrastructure/IAM-POLICY-TEMPLATES.md +176 -0
  39. package/infrastructure/README-TESTING.md +332 -0
  40. package/infrastructure/README.md +421 -0
  41. package/infrastructure/WEBSOCKET-CONFIGURATION.md +105 -0
  42. package/infrastructure/__tests__/fixtures/mock-aws-resources.js +391 -0
  43. package/infrastructure/__tests__/helpers/test-utils.js +277 -0
  44. package/infrastructure/aws-discovery.js +568 -0
  45. package/infrastructure/aws-discovery.test.js +373 -0
  46. package/infrastructure/build-time-discovery.js +206 -0
  47. package/infrastructure/build-time-discovery.test.js +375 -0
  48. package/infrastructure/create-frigg-infrastructure.js +3 -5
  49. package/infrastructure/frigg-deployment-iam-stack.yaml +379 -0
  50. package/infrastructure/iam-generator.js +687 -0
  51. package/infrastructure/iam-generator.test.js +169 -0
  52. package/infrastructure/iam-policy-basic.json +212 -0
  53. package/infrastructure/iam-policy-full.json +282 -0
  54. package/infrastructure/integration.test.js +383 -0
  55. package/infrastructure/run-discovery.js +110 -0
  56. package/infrastructure/serverless-template.js +923 -113
  57. package/infrastructure/serverless-template.test.js +541 -0
  58. package/management-ui/.eslintrc.js +22 -0
  59. package/management-ui/README.md +203 -0
  60. package/management-ui/components.json +21 -0
  61. package/management-ui/docs/phase2-integration-guide.md +320 -0
  62. package/management-ui/index.html +13 -0
  63. package/management-ui/package-lock.json +16517 -0
  64. package/management-ui/package.json +76 -0
  65. package/management-ui/packages/devtools/frigg-cli/ui-command/index.js +302 -0
  66. package/management-ui/postcss.config.js +6 -0
  67. package/management-ui/server/api/backend.js +256 -0
  68. package/management-ui/server/api/cli.js +315 -0
  69. package/management-ui/server/api/codegen.js +663 -0
  70. package/management-ui/server/api/connections.js +857 -0
  71. package/management-ui/server/api/discovery.js +185 -0
  72. package/management-ui/server/api/environment/index.js +1 -0
  73. package/management-ui/server/api/environment/router.js +378 -0
  74. package/management-ui/server/api/environment.js +328 -0
  75. package/management-ui/server/api/integrations.js +876 -0
  76. package/management-ui/server/api/logs.js +248 -0
  77. package/management-ui/server/api/monitoring.js +282 -0
  78. package/management-ui/server/api/open-ide.js +31 -0
  79. package/management-ui/server/api/project.js +1029 -0
  80. package/management-ui/server/api/users/sessions.js +371 -0
  81. package/management-ui/server/api/users/simulation.js +254 -0
  82. package/management-ui/server/api/users.js +362 -0
  83. package/management-ui/server/api-contract.md +275 -0
  84. package/management-ui/server/index.js +873 -0
  85. package/management-ui/server/middleware/errorHandler.js +93 -0
  86. package/management-ui/server/middleware/security.js +32 -0
  87. package/management-ui/server/processManager.js +296 -0
  88. package/management-ui/server/server.js +346 -0
  89. package/management-ui/server/services/aws-monitor.js +413 -0
  90. package/management-ui/server/services/npm-registry.js +347 -0
  91. package/management-ui/server/services/template-engine.js +538 -0
  92. package/management-ui/server/utils/cliIntegration.js +220 -0
  93. package/management-ui/server/utils/environment/auditLogger.js +471 -0
  94. package/management-ui/server/utils/environment/awsParameterStore.js +264 -0
  95. package/management-ui/server/utils/environment/encryption.js +278 -0
  96. package/management-ui/server/utils/environment/envFileManager.js +286 -0
  97. package/management-ui/server/utils/import-commonjs.js +28 -0
  98. package/management-ui/server/utils/response.js +83 -0
  99. package/management-ui/server/websocket/handler.js +325 -0
  100. package/management-ui/src/App.jsx +109 -0
  101. package/management-ui/src/assets/FriggLogo.svg +1 -0
  102. package/management-ui/src/components/AppRouter.jsx +65 -0
  103. package/management-ui/src/components/Button.jsx +70 -0
  104. package/management-ui/src/components/Card.jsx +97 -0
  105. package/management-ui/src/components/EnvironmentCompare.jsx +400 -0
  106. package/management-ui/src/components/EnvironmentEditor.jsx +372 -0
  107. package/management-ui/src/components/EnvironmentImportExport.jsx +469 -0
  108. package/management-ui/src/components/EnvironmentSchema.jsx +491 -0
  109. package/management-ui/src/components/EnvironmentSecurity.jsx +463 -0
  110. package/management-ui/src/components/ErrorBoundary.jsx +73 -0
  111. package/management-ui/src/components/IntegrationCard.jsx +481 -0
  112. package/management-ui/src/components/IntegrationCardEnhanced.jsx +770 -0
  113. package/management-ui/src/components/IntegrationExplorer.jsx +379 -0
  114. package/management-ui/src/components/IntegrationStatus.jsx +336 -0
  115. package/management-ui/src/components/Layout.jsx +716 -0
  116. package/management-ui/src/components/LoadingSpinner.jsx +113 -0
  117. package/management-ui/src/components/RepositoryPicker.jsx +248 -0
  118. package/management-ui/src/components/SessionMonitor.jsx +350 -0
  119. package/management-ui/src/components/StatusBadge.jsx +208 -0
  120. package/management-ui/src/components/UserContextSwitcher.jsx +212 -0
  121. package/management-ui/src/components/UserSimulation.jsx +327 -0
  122. package/management-ui/src/components/Welcome.jsx +434 -0
  123. package/management-ui/src/components/codegen/APIEndpointGenerator.jsx +637 -0
  124. package/management-ui/src/components/codegen/APIModuleSelector.jsx +227 -0
  125. package/management-ui/src/components/codegen/CodeGenerationWizard.jsx +247 -0
  126. package/management-ui/src/components/codegen/CodePreviewEditor.jsx +316 -0
  127. package/management-ui/src/components/codegen/DynamicModuleForm.jsx +271 -0
  128. package/management-ui/src/components/codegen/FormBuilder.jsx +737 -0
  129. package/management-ui/src/components/codegen/IntegrationGenerator.jsx +855 -0
  130. package/management-ui/src/components/codegen/ProjectScaffoldWizard.jsx +797 -0
  131. package/management-ui/src/components/codegen/SchemaBuilder.jsx +303 -0
  132. package/management-ui/src/components/codegen/TemplateSelector.jsx +586 -0
  133. package/management-ui/src/components/codegen/index.js +10 -0
  134. package/management-ui/src/components/connections/ConnectionConfigForm.jsx +362 -0
  135. package/management-ui/src/components/connections/ConnectionHealthMonitor.jsx +182 -0
  136. package/management-ui/src/components/connections/ConnectionTester.jsx +200 -0
  137. package/management-ui/src/components/connections/EntityRelationshipMapper.jsx +292 -0
  138. package/management-ui/src/components/connections/OAuthFlow.jsx +204 -0
  139. package/management-ui/src/components/connections/index.js +5 -0
  140. package/management-ui/src/components/index.js +21 -0
  141. package/management-ui/src/components/monitoring/APIGatewayMetrics.jsx +222 -0
  142. package/management-ui/src/components/monitoring/LambdaMetrics.jsx +169 -0
  143. package/management-ui/src/components/monitoring/MetricsChart.jsx +197 -0
  144. package/management-ui/src/components/monitoring/MonitoringDashboard.jsx +393 -0
  145. package/management-ui/src/components/monitoring/SQSMetrics.jsx +246 -0
  146. package/management-ui/src/components/monitoring/index.js +6 -0
  147. package/management-ui/src/components/monitoring/monitoring.css +218 -0
  148. package/management-ui/src/components/theme-provider.jsx +52 -0
  149. package/management-ui/src/components/theme-toggle.jsx +39 -0
  150. package/management-ui/src/components/ui/badge.tsx +36 -0
  151. package/management-ui/src/components/ui/button.test.jsx +56 -0
  152. package/management-ui/src/components/ui/button.tsx +57 -0
  153. package/management-ui/src/components/ui/card.tsx +76 -0
  154. package/management-ui/src/components/ui/dropdown-menu.tsx +199 -0
  155. package/management-ui/src/components/ui/select.tsx +157 -0
  156. package/management-ui/src/components/ui/skeleton.jsx +15 -0
  157. package/management-ui/src/hooks/useFrigg.jsx +601 -0
  158. package/management-ui/src/hooks/useSocket.jsx +58 -0
  159. package/management-ui/src/index.css +193 -0
  160. package/management-ui/src/lib/utils.ts +6 -0
  161. package/management-ui/src/main.jsx +10 -0
  162. package/management-ui/src/pages/CodeGeneration.jsx +14 -0
  163. package/management-ui/src/pages/Connections.jsx +252 -0
  164. package/management-ui/src/pages/ConnectionsEnhanced.jsx +633 -0
  165. package/management-ui/src/pages/Dashboard.jsx +311 -0
  166. package/management-ui/src/pages/Environment.jsx +314 -0
  167. package/management-ui/src/pages/IntegrationConfigure.jsx +669 -0
  168. package/management-ui/src/pages/IntegrationDiscovery.jsx +567 -0
  169. package/management-ui/src/pages/IntegrationTest.jsx +742 -0
  170. package/management-ui/src/pages/Integrations.jsx +253 -0
  171. package/management-ui/src/pages/Monitoring.jsx +17 -0
  172. package/management-ui/src/pages/Simulation.jsx +155 -0
  173. package/management-ui/src/pages/Users.jsx +492 -0
  174. package/management-ui/src/services/api.js +41 -0
  175. package/management-ui/src/services/apiModuleService.js +193 -0
  176. package/management-ui/src/services/websocket-handlers.js +120 -0
  177. package/management-ui/src/test/api/project.test.js +273 -0
  178. package/management-ui/src/test/components/Welcome.test.jsx +378 -0
  179. package/management-ui/src/test/mocks/server.js +178 -0
  180. package/management-ui/src/test/setup.js +61 -0
  181. package/management-ui/src/test/utils/test-utils.jsx +134 -0
  182. package/management-ui/src/utils/repository.js +98 -0
  183. package/management-ui/src/utils/repository.test.js +118 -0
  184. package/management-ui/src/workflows/phase2-integration-workflows.js +884 -0
  185. package/management-ui/tailwind.config.js +63 -0
  186. package/management-ui/tsconfig.json +37 -0
  187. package/management-ui/tsconfig.node.json +10 -0
  188. package/management-ui/vite.config.js +26 -0
  189. package/management-ui/vitest.config.js +38 -0
  190. package/package.json +16 -9
  191. package/infrastructure/app-handler-helpers.js +0 -57
  192. package/infrastructure/backend-utils.js +0 -90
  193. package/infrastructure/routers/auth.js +0 -26
  194. package/infrastructure/routers/integration-defined-routers.js +0 -37
  195. package/infrastructure/routers/middleware/loadUser.js +0 -15
  196. package/infrastructure/routers/middleware/requireLoggedInUser.js +0 -12
  197. package/infrastructure/routers/user.js +0 -41
  198. package/infrastructure/routers/websocket.js +0 -55
  199. package/infrastructure/workers/integration-defined-workers.js +0 -24
@@ -0,0 +1,418 @@
1
+ const { installCommand } = require('../../../install-command');
2
+ const { CommandTester } = require('../../utils/command-tester');
3
+ const { MockFactory } = require('../../utils/mock-factory');
4
+ const { TestFixtures } = require('../../utils/test-fixtures');
5
+
6
+ describe('CLI Command: install', () => {
7
+ let commandTester;
8
+ let mocks;
9
+
10
+ beforeEach(() => {
11
+ mocks = MockFactory.createMockEnvironment();
12
+ commandTester = new CommandTester({
13
+ name: 'install <apiModuleName>',
14
+ description: 'Install an API module',
15
+ action: installCommand,
16
+ options: [
17
+ { flags: '--app-path <path>', description: 'path to Frigg application directory' },
18
+ { flags: '--config <path>', description: 'path to Frigg configuration file' },
19
+ { flags: '--app <path>', description: 'alias for --app-path' }
20
+ ]
21
+ });
22
+ });
23
+
24
+ afterEach(() => {
25
+ jest.clearAllMocks();
26
+ commandTester.reset();
27
+ });
28
+
29
+ describe('Success Cases', () => {
30
+ it('should successfully install an API module with default configuration', async () => {
31
+ // Arrange
32
+ const moduleName = 'salesforce';
33
+ const expectedPackage = '@friggframework/api-module-salesforce';
34
+
35
+ commandTester
36
+ .mock('./install-command/validate-package', {
37
+ validatePackageExists: jest.fn().mockResolvedValue(true)
38
+ })
39
+ .mock('@friggframework/core', {
40
+ findNearestBackendPackageJson: jest.fn().mockReturnValue('/mock/backend/package.json'),
41
+ validateBackendPath: jest.fn().mockReturnValue(true)
42
+ })
43
+ .mock('./install-command/install-package', {
44
+ installPackage: jest.fn().mockResolvedValue(true)
45
+ })
46
+ .mock('./install-command/integration-file', {
47
+ createIntegrationFile: jest.fn().mockResolvedValue(true)
48
+ })
49
+ .mock('./install-command/backend-js', {
50
+ updateBackendJsFile: jest.fn().mockResolvedValue(true)
51
+ })
52
+ .mock('./install-command/commit-changes', {
53
+ commitChanges: jest.fn().mockResolvedValue(true)
54
+ })
55
+ .mock('./install-command/logger', mocks.logger);
56
+
57
+ // Act
58
+ const result = await commandTester.execute([moduleName]);
59
+
60
+ // Assert
61
+ expect(result.success).toBe(true);
62
+ expect(result.exitCode).toBe(0);
63
+ });
64
+
65
+ it('should successfully install with custom app path', async () => {
66
+ // Arrange
67
+ const moduleName = 'hubspot';
68
+ const customPath = '/custom/app/path';
69
+
70
+ commandTester
71
+ .mock('./install-command/validate-package', {
72
+ validatePackageExists: jest.fn().mockResolvedValue(true)
73
+ })
74
+ .mock('@friggframework/core', {
75
+ findNearestBackendPackageJson: jest.fn().mockReturnValue('/custom/backend/package.json'),
76
+ validateBackendPath: jest.fn().mockReturnValue(true)
77
+ })
78
+ .mock('./install-command/install-package', {
79
+ installPackage: jest.fn().mockResolvedValue(true)
80
+ })
81
+ .mock('./install-command/integration-file', {
82
+ createIntegrationFile: jest.fn().mockResolvedValue(true)
83
+ })
84
+ .mock('./install-command/backend-js', {
85
+ updateBackendJsFile: jest.fn().mockResolvedValue(true)
86
+ })
87
+ .mock('./install-command/commit-changes', {
88
+ commitChanges: jest.fn().mockResolvedValue(true)
89
+ })
90
+ .mock('./install-command/logger', mocks.logger);
91
+
92
+ // Act
93
+ const result = await commandTester.execute([moduleName, '--app-path', customPath]);
94
+
95
+ // Assert
96
+ expect(result.success).toBe(true);
97
+ expect(result.exitCode).toBe(0);
98
+ });
99
+
100
+ it('should handle module names with special characters', async () => {
101
+ // Arrange
102
+ const moduleName = 'google-calendar';
103
+ const expectedPackage = '@friggframework/api-module-google-calendar';
104
+
105
+ commandTester
106
+ .mock('./install-command/validate-package', {
107
+ validatePackageExists: jest.fn().mockResolvedValue(true)
108
+ })
109
+ .mock('@friggframework/core', {
110
+ findNearestBackendPackageJson: jest.fn().mockReturnValue('/mock/backend/package.json'),
111
+ validateBackendPath: jest.fn().mockReturnValue(true)
112
+ })
113
+ .mock('./install-command/install-package', {
114
+ installPackage: jest.fn().mockResolvedValue(true)
115
+ })
116
+ .mock('./install-command/integration-file', {
117
+ createIntegrationFile: jest.fn().mockResolvedValue(true)
118
+ })
119
+ .mock('./install-command/backend-js', {
120
+ updateBackendJsFile: jest.fn().mockResolvedValue(true)
121
+ })
122
+ .mock('./install-command/commit-changes', {
123
+ commitChanges: jest.fn().mockResolvedValue(true)
124
+ })
125
+ .mock('./install-command/logger', mocks.logger);
126
+
127
+ // Act
128
+ const result = await commandTester.execute([moduleName]);
129
+
130
+ // Assert
131
+ expect(result.success).toBe(true);
132
+ expect(result.exitCode).toBe(0);
133
+ });
134
+ });
135
+
136
+ describe('Error Cases', () => {
137
+ it('should handle package not found error', async () => {
138
+ // Arrange
139
+ const moduleName = 'non-existent-module';
140
+
141
+ commandTester
142
+ .mock('./install-command/validate-package', {
143
+ validatePackageExists: jest.fn().mockRejectedValue(new Error('Package not found'))
144
+ })
145
+ .mock('./install-command/logger', mocks.logger);
146
+
147
+ // Act
148
+ const result = await commandTester.execute([moduleName]);
149
+
150
+ // Assert
151
+ expect(result.success).toBe(false);
152
+ expect(result.exitCode).toBe(1);
153
+ });
154
+
155
+ it('should handle invalid backend path error', async () => {
156
+ // Arrange
157
+ const moduleName = 'salesforce';
158
+
159
+ commandTester
160
+ .mock('./install-command/validate-package', {
161
+ validatePackageExists: jest.fn().mockResolvedValue(true)
162
+ })
163
+ .mock('@friggframework/core', {
164
+ findNearestBackendPackageJson: jest.fn().mockReturnValue('/invalid/path'),
165
+ validateBackendPath: jest.fn().mockImplementation(() => {
166
+ throw new Error('Invalid backend path');
167
+ })
168
+ })
169
+ .mock('./install-command/logger', mocks.logger);
170
+
171
+ // Act
172
+ const result = await commandTester.execute([moduleName]);
173
+
174
+ // Assert
175
+ expect(result.success).toBe(false);
176
+ expect(result.exitCode).toBe(1);
177
+ });
178
+
179
+ it('should handle installation failure', async () => {
180
+ // Arrange
181
+ const moduleName = 'salesforce';
182
+
183
+ commandTester
184
+ .mock('./install-command/validate-package', {
185
+ validatePackageExists: jest.fn().mockResolvedValue(true)
186
+ })
187
+ .mock('@friggframework/core', {
188
+ findNearestBackendPackageJson: jest.fn().mockReturnValue('/mock/backend/package.json'),
189
+ validateBackendPath: jest.fn().mockReturnValue(true)
190
+ })
191
+ .mock('./install-command/install-package', {
192
+ installPackage: jest.fn().mockRejectedValue(new Error('Installation failed'))
193
+ })
194
+ .mock('./install-command/logger', mocks.logger);
195
+
196
+ // Act
197
+ const result = await commandTester.execute([moduleName]);
198
+
199
+ // Assert
200
+ expect(result.success).toBe(false);
201
+ expect(result.exitCode).toBe(1);
202
+ });
203
+
204
+ it('should handle integration file creation failure', async () => {
205
+ // Arrange
206
+ const moduleName = 'salesforce';
207
+
208
+ commandTester
209
+ .mock('./install-command/validate-package', {
210
+ validatePackageExists: jest.fn().mockResolvedValue(true)
211
+ })
212
+ .mock('@friggframework/core', {
213
+ findNearestBackendPackageJson: jest.fn().mockReturnValue('/mock/backend/package.json'),
214
+ validateBackendPath: jest.fn().mockReturnValue(true)
215
+ })
216
+ .mock('./install-command/install-package', {
217
+ installPackage: jest.fn().mockResolvedValue(true)
218
+ })
219
+ .mock('./install-command/integration-file', {
220
+ createIntegrationFile: jest.fn().mockRejectedValue(new Error('File creation failed'))
221
+ })
222
+ .mock('./install-command/logger', mocks.logger);
223
+
224
+ // Act
225
+ const result = await commandTester.execute([moduleName]);
226
+
227
+ // Assert
228
+ expect(result.success).toBe(false);
229
+ expect(result.exitCode).toBe(1);
230
+ });
231
+ });
232
+
233
+ describe('Edge Cases', () => {
234
+ it('should handle empty module name', async () => {
235
+ // Arrange
236
+ commandTester
237
+ .mock('./install-command/logger', mocks.logger);
238
+
239
+ // Act
240
+ const result = await commandTester.execute(['']);
241
+
242
+ // Assert
243
+ expect(result.success).toBe(false);
244
+ expect(result.exitCode).toBe(1);
245
+ });
246
+
247
+ it('should handle module name with invalid characters', async () => {
248
+ // Arrange
249
+ const moduleName = 'invalid@module#name';
250
+
251
+ commandTester
252
+ .mock('./install-command/validate-package', {
253
+ validatePackageExists: jest.fn().mockRejectedValue(new Error('Invalid package name'))
254
+ })
255
+ .mock('./install-command/logger', mocks.logger);
256
+
257
+ // Act
258
+ const result = await commandTester.execute([moduleName]);
259
+
260
+ // Assert
261
+ expect(result.success).toBe(false);
262
+ expect(result.exitCode).toBe(1);
263
+ });
264
+
265
+ it('should handle network timeout during package validation', async () => {
266
+ // Arrange
267
+ const moduleName = 'salesforce';
268
+
269
+ commandTester
270
+ .mock('./install-command/validate-package', {
271
+ validatePackageExists: jest.fn().mockRejectedValue(new Error('Network timeout'))
272
+ })
273
+ .mock('./install-command/logger', mocks.logger);
274
+
275
+ // Act
276
+ const result = await commandTester.execute([moduleName]);
277
+
278
+ // Assert
279
+ expect(result.success).toBe(false);
280
+ expect(result.exitCode).toBe(1);
281
+ });
282
+
283
+ it('should handle permission denied during file operations', async () => {
284
+ // Arrange
285
+ const moduleName = 'salesforce';
286
+
287
+ commandTester
288
+ .mock('./install-command/validate-package', {
289
+ validatePackageExists: jest.fn().mockResolvedValue(true)
290
+ })
291
+ .mock('@friggframework/core', {
292
+ findNearestBackendPackageJson: jest.fn().mockReturnValue('/mock/backend/package.json'),
293
+ validateBackendPath: jest.fn().mockReturnValue(true)
294
+ })
295
+ .mock('./install-command/install-package', {
296
+ installPackage: jest.fn().mockResolvedValue(true)
297
+ })
298
+ .mock('./install-command/integration-file', {
299
+ createIntegrationFile: jest.fn().mockRejectedValue(new Error('EACCES: permission denied'))
300
+ })
301
+ .mock('./install-command/logger', mocks.logger);
302
+
303
+ // Act
304
+ const result = await commandTester.execute([moduleName]);
305
+
306
+ // Assert
307
+ expect(result.success).toBe(false);
308
+ expect(result.exitCode).toBe(1);
309
+ });
310
+ });
311
+
312
+ describe('Option Validation', () => {
313
+ it('should handle --app-path option', async () => {
314
+ // Arrange
315
+ const moduleName = 'salesforce';
316
+ const customPath = '/custom/path';
317
+
318
+ commandTester
319
+ .mock('./install-command/validate-package', {
320
+ validatePackageExists: jest.fn().mockResolvedValue(true)
321
+ })
322
+ .mock('@friggframework/core', {
323
+ findNearestBackendPackageJson: jest.fn().mockReturnValue('/custom/backend/package.json'),
324
+ validateBackendPath: jest.fn().mockReturnValue(true)
325
+ })
326
+ .mock('./install-command/install-package', {
327
+ installPackage: jest.fn().mockResolvedValue(true)
328
+ })
329
+ .mock('./install-command/integration-file', {
330
+ createIntegrationFile: jest.fn().mockResolvedValue(true)
331
+ })
332
+ .mock('./install-command/backend-js', {
333
+ updateBackendJsFile: jest.fn().mockResolvedValue(true)
334
+ })
335
+ .mock('./install-command/commit-changes', {
336
+ commitChanges: jest.fn().mockResolvedValue(true)
337
+ })
338
+ .mock('./install-command/logger', mocks.logger);
339
+
340
+ // Act
341
+ const result = await commandTester.execute([moduleName, '--app-path', customPath]);
342
+
343
+ // Assert
344
+ expect(result.success).toBe(true);
345
+ expect(result.args).toEqual([moduleName, '--app-path', customPath]);
346
+ });
347
+
348
+ it('should handle --config option', async () => {
349
+ // Arrange
350
+ const moduleName = 'salesforce';
351
+ const configPath = '/custom/config.json';
352
+
353
+ commandTester
354
+ .mock('./install-command/validate-package', {
355
+ validatePackageExists: jest.fn().mockResolvedValue(true)
356
+ })
357
+ .mock('@friggframework/core', {
358
+ findNearestBackendPackageJson: jest.fn().mockReturnValue('/mock/backend/package.json'),
359
+ validateBackendPath: jest.fn().mockReturnValue(true)
360
+ })
361
+ .mock('./install-command/install-package', {
362
+ installPackage: jest.fn().mockResolvedValue(true)
363
+ })
364
+ .mock('./install-command/integration-file', {
365
+ createIntegrationFile: jest.fn().mockResolvedValue(true)
366
+ })
367
+ .mock('./install-command/backend-js', {
368
+ updateBackendJsFile: jest.fn().mockResolvedValue(true)
369
+ })
370
+ .mock('./install-command/commit-changes', {
371
+ commitChanges: jest.fn().mockResolvedValue(true)
372
+ })
373
+ .mock('./install-command/logger', mocks.logger);
374
+
375
+ // Act
376
+ const result = await commandTester.execute([moduleName, '--config', configPath]);
377
+
378
+ // Assert
379
+ expect(result.success).toBe(true);
380
+ expect(result.args).toEqual([moduleName, '--config', configPath]);
381
+ });
382
+
383
+ it('should handle --app alias for --app-path', async () => {
384
+ // Arrange
385
+ const moduleName = 'salesforce';
386
+ const customPath = '/custom/path';
387
+
388
+ commandTester
389
+ .mock('./install-command/validate-package', {
390
+ validatePackageExists: jest.fn().mockResolvedValue(true)
391
+ })
392
+ .mock('@friggframework/core', {
393
+ findNearestBackendPackageJson: jest.fn().mockReturnValue('/custom/backend/package.json'),
394
+ validateBackendPath: jest.fn().mockReturnValue(true)
395
+ })
396
+ .mock('./install-command/install-package', {
397
+ installPackage: jest.fn().mockResolvedValue(true)
398
+ })
399
+ .mock('./install-command/integration-file', {
400
+ createIntegrationFile: jest.fn().mockResolvedValue(true)
401
+ })
402
+ .mock('./install-command/backend-js', {
403
+ updateBackendJsFile: jest.fn().mockResolvedValue(true)
404
+ })
405
+ .mock('./install-command/commit-changes', {
406
+ commitChanges: jest.fn().mockResolvedValue(true)
407
+ })
408
+ .mock('./install-command/logger', mocks.logger);
409
+
410
+ // Act
411
+ const result = await commandTester.execute([moduleName, '--app', customPath]);
412
+
413
+ // Assert
414
+ expect(result.success).toBe(true);
415
+ expect(result.args).toEqual([moduleName, '--app', customPath]);
416
+ });
417
+ });
418
+ });