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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/infrastructure/ARCHITECTURE.md +487 -0
  2. package/infrastructure/HEALTH.md +468 -0
  3. package/infrastructure/README.md +51 -0
  4. package/infrastructure/__tests__/postgres-config.test.js +914 -0
  5. package/infrastructure/__tests__/template-generation.test.js +687 -0
  6. package/infrastructure/create-frigg-infrastructure.js +1 -1
  7. package/infrastructure/docs/POSTGRES-CONFIGURATION.md +630 -0
  8. package/infrastructure/{DEPLOYMENT-INSTRUCTIONS.md → docs/deployment-instructions.md} +3 -3
  9. package/infrastructure/{IAM-POLICY-TEMPLATES.md → docs/iam-policy-templates.md} +9 -10
  10. package/infrastructure/domains/database/aurora-builder.js +809 -0
  11. package/infrastructure/domains/database/aurora-builder.test.js +950 -0
  12. package/infrastructure/domains/database/aurora-discovery.js +87 -0
  13. package/infrastructure/domains/database/aurora-discovery.test.js +188 -0
  14. package/infrastructure/domains/database/aurora-resolver.js +210 -0
  15. package/infrastructure/domains/database/aurora-resolver.test.js +347 -0
  16. package/infrastructure/domains/database/migration-builder.js +633 -0
  17. package/infrastructure/domains/database/migration-builder.test.js +294 -0
  18. package/infrastructure/domains/database/migration-resolver.js +163 -0
  19. package/infrastructure/domains/database/migration-resolver.test.js +337 -0
  20. package/infrastructure/domains/health/application/ports/IPropertyReconciler.js +164 -0
  21. package/infrastructure/domains/health/application/ports/IResourceDetector.js +129 -0
  22. package/infrastructure/domains/health/application/ports/IResourceImporter.js +142 -0
  23. package/infrastructure/domains/health/application/ports/IStackRepository.js +131 -0
  24. package/infrastructure/domains/health/application/ports/index.js +26 -0
  25. package/infrastructure/domains/health/application/use-cases/__tests__/execute-resource-import-use-case.test.js +679 -0
  26. package/infrastructure/domains/health/application/use-cases/__tests__/mismatch-analyzer-method-name.test.js +167 -0
  27. package/infrastructure/domains/health/application/use-cases/__tests__/repair-via-import-use-case.test.js +1130 -0
  28. package/infrastructure/domains/health/application/use-cases/execute-resource-import-use-case.js +221 -0
  29. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.js +152 -0
  30. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.test.js +343 -0
  31. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.js +535 -0
  32. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.test.js +376 -0
  33. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.js +213 -0
  34. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.test.js +441 -0
  35. package/infrastructure/domains/health/docs/ACME-DEV-DRIFT-ANALYSIS.md +267 -0
  36. package/infrastructure/domains/health/docs/BUILD-VS-DEPLOYED-TEMPLATE-ANALYSIS.md +324 -0
  37. package/infrastructure/domains/health/docs/ORPHAN-DETECTION-ANALYSIS.md +386 -0
  38. package/infrastructure/domains/health/docs/SPEC-CLEANUP-COMMAND.md +1419 -0
  39. package/infrastructure/domains/health/docs/TDD-IMPLEMENTATION-SUMMARY.md +391 -0
  40. package/infrastructure/domains/health/docs/TEMPLATE-COMPARISON-IMPLEMENTATION.md +551 -0
  41. package/infrastructure/domains/health/domain/entities/issue.js +299 -0
  42. package/infrastructure/domains/health/domain/entities/issue.test.js +528 -0
  43. package/infrastructure/domains/health/domain/entities/property-mismatch.js +108 -0
  44. package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +275 -0
  45. package/infrastructure/domains/health/domain/entities/resource.js +159 -0
  46. package/infrastructure/domains/health/domain/entities/resource.test.js +432 -0
  47. package/infrastructure/domains/health/domain/entities/stack-health-report.js +306 -0
  48. package/infrastructure/domains/health/domain/entities/stack-health-report.test.js +601 -0
  49. package/infrastructure/domains/health/domain/services/__tests__/health-score-percentage-based.test.js +380 -0
  50. package/infrastructure/domains/health/domain/services/__tests__/import-progress-monitor.test.js +971 -0
  51. package/infrastructure/domains/health/domain/services/__tests__/import-template-generator.test.js +1150 -0
  52. package/infrastructure/domains/health/domain/services/__tests__/logical-id-mapper.test.js +672 -0
  53. package/infrastructure/domains/health/domain/services/__tests__/template-parser.test.js +496 -0
  54. package/infrastructure/domains/health/domain/services/__tests__/update-progress-monitor.test.js +419 -0
  55. package/infrastructure/domains/health/domain/services/health-score-calculator.js +248 -0
  56. package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +504 -0
  57. package/infrastructure/domains/health/domain/services/import-progress-monitor.js +195 -0
  58. package/infrastructure/domains/health/domain/services/import-template-generator.js +435 -0
  59. package/infrastructure/domains/health/domain/services/logical-id-mapper.js +345 -0
  60. package/infrastructure/domains/health/domain/services/mismatch-analyzer.js +234 -0
  61. package/infrastructure/domains/health/domain/services/mismatch-analyzer.test.js +431 -0
  62. package/infrastructure/domains/health/domain/services/property-mutability-config.js +382 -0
  63. package/infrastructure/domains/health/domain/services/template-parser.js +245 -0
  64. package/infrastructure/domains/health/domain/services/update-progress-monitor.js +192 -0
  65. package/infrastructure/domains/health/domain/value-objects/health-score.js +138 -0
  66. package/infrastructure/domains/health/domain/value-objects/health-score.test.js +267 -0
  67. package/infrastructure/domains/health/domain/value-objects/property-mutability.js +161 -0
  68. package/infrastructure/domains/health/domain/value-objects/property-mutability.test.js +198 -0
  69. package/infrastructure/domains/health/domain/value-objects/resource-state.js +167 -0
  70. package/infrastructure/domains/health/domain/value-objects/resource-state.test.js +196 -0
  71. package/infrastructure/domains/health/domain/value-objects/stack-identifier.js +192 -0
  72. package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +262 -0
  73. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-cfn-tagged.test.js +312 -0
  74. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-multi-stack.test.js +367 -0
  75. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-relationship-analysis.test.js +432 -0
  76. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.js +784 -0
  77. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.test.js +1133 -0
  78. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.js +565 -0
  79. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +554 -0
  80. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.js +318 -0
  81. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.test.js +398 -0
  82. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +777 -0
  83. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.test.js +580 -0
  84. package/infrastructure/domains/integration/integration-builder.js +397 -0
  85. package/infrastructure/domains/integration/integration-builder.test.js +593 -0
  86. package/infrastructure/domains/integration/integration-resolver.js +170 -0
  87. package/infrastructure/domains/integration/integration-resolver.test.js +369 -0
  88. package/infrastructure/domains/integration/websocket-builder.js +69 -0
  89. package/infrastructure/domains/integration/websocket-builder.test.js +195 -0
  90. package/infrastructure/domains/networking/vpc-builder.js +1829 -0
  91. package/infrastructure/domains/networking/vpc-builder.test.js +1262 -0
  92. package/infrastructure/domains/networking/vpc-discovery.js +177 -0
  93. package/infrastructure/domains/networking/vpc-discovery.test.js +350 -0
  94. package/infrastructure/domains/networking/vpc-resolver.js +324 -0
  95. package/infrastructure/domains/networking/vpc-resolver.test.js +501 -0
  96. package/infrastructure/domains/parameters/ssm-builder.js +79 -0
  97. package/infrastructure/domains/parameters/ssm-builder.test.js +189 -0
  98. package/infrastructure/domains/parameters/ssm-discovery.js +84 -0
  99. package/infrastructure/domains/parameters/ssm-discovery.test.js +210 -0
  100. package/infrastructure/{iam-generator.js → domains/security/iam-generator.js} +2 -2
  101. package/infrastructure/domains/security/kms-builder.js +366 -0
  102. package/infrastructure/domains/security/kms-builder.test.js +374 -0
  103. package/infrastructure/domains/security/kms-discovery.js +80 -0
  104. package/infrastructure/domains/security/kms-discovery.test.js +177 -0
  105. package/infrastructure/domains/security/kms-resolver.js +96 -0
  106. package/infrastructure/domains/security/kms-resolver.test.js +216 -0
  107. package/infrastructure/domains/shared/base-builder.js +112 -0
  108. package/infrastructure/domains/shared/base-resolver.js +186 -0
  109. package/infrastructure/domains/shared/base-resolver.test.js +305 -0
  110. package/infrastructure/domains/shared/builder-orchestrator.js +212 -0
  111. package/infrastructure/domains/shared/builder-orchestrator.test.js +213 -0
  112. package/infrastructure/domains/shared/cloudformation-discovery-v2.js +334 -0
  113. package/infrastructure/domains/shared/cloudformation-discovery.js +375 -0
  114. package/infrastructure/domains/shared/cloudformation-discovery.test.js +590 -0
  115. package/infrastructure/domains/shared/environment-builder.js +119 -0
  116. package/infrastructure/domains/shared/environment-builder.test.js +247 -0
  117. package/infrastructure/domains/shared/providers/aws-provider-adapter.js +544 -0
  118. package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +377 -0
  119. package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +93 -0
  120. package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +136 -0
  121. package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +82 -0
  122. package/infrastructure/domains/shared/providers/provider-factory.js +108 -0
  123. package/infrastructure/domains/shared/providers/provider-factory.test.js +170 -0
  124. package/infrastructure/domains/shared/resource-discovery.js +192 -0
  125. package/infrastructure/domains/shared/resource-discovery.test.js +552 -0
  126. package/infrastructure/domains/shared/types/app-definition.js +205 -0
  127. package/infrastructure/domains/shared/types/discovery-result.js +106 -0
  128. package/infrastructure/domains/shared/types/discovery-result.test.js +258 -0
  129. package/infrastructure/domains/shared/types/index.js +46 -0
  130. package/infrastructure/domains/shared/types/resource-ownership.js +108 -0
  131. package/infrastructure/domains/shared/types/resource-ownership.test.js +101 -0
  132. package/infrastructure/domains/shared/utilities/base-definition-factory.js +380 -0
  133. package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
  134. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +248 -0
  135. package/infrastructure/domains/shared/utilities/handler-path-resolver.js +134 -0
  136. package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +268 -0
  137. package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +55 -0
  138. package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +138 -0
  139. package/infrastructure/{env-validator.js → domains/shared/validation/env-validator.js} +2 -1
  140. package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
  141. package/infrastructure/esbuild.config.js +53 -0
  142. package/infrastructure/infrastructure-composer.js +87 -0
  143. package/infrastructure/{serverless-template.test.js → infrastructure-composer.test.js} +115 -24
  144. package/infrastructure/scripts/build-prisma-layer.js +553 -0
  145. package/infrastructure/scripts/build-prisma-layer.test.js +102 -0
  146. package/infrastructure/{build-time-discovery.js → scripts/build-time-discovery.js} +80 -48
  147. package/infrastructure/{build-time-discovery.test.js → scripts/build-time-discovery.test.js} +5 -4
  148. package/layers/prisma/nodejs/package.json +8 -0
  149. package/management-ui/server/utils/cliIntegration.js +1 -1
  150. package/management-ui/server/utils/environment/awsParameterStore.js +29 -18
  151. package/package.json +11 -11
  152. package/frigg-cli/.eslintrc.js +0 -141
  153. package/frigg-cli/__tests__/unit/commands/build.test.js +0 -251
  154. package/frigg-cli/__tests__/unit/commands/db-setup.test.js +0 -548
  155. package/frigg-cli/__tests__/unit/commands/install.test.js +0 -400
  156. package/frigg-cli/__tests__/unit/commands/ui.test.js +0 -346
  157. package/frigg-cli/__tests__/unit/utils/database-validator.test.js +0 -366
  158. package/frigg-cli/__tests__/unit/utils/error-messages.test.js +0 -304
  159. package/frigg-cli/__tests__/unit/utils/prisma-runner.test.js +0 -486
  160. package/frigg-cli/__tests__/utils/mock-factory.js +0 -270
  161. package/frigg-cli/__tests__/utils/prisma-mock.js +0 -194
  162. package/frigg-cli/__tests__/utils/test-fixtures.js +0 -463
  163. package/frigg-cli/__tests__/utils/test-setup.js +0 -287
  164. package/frigg-cli/build-command/index.js +0 -65
  165. package/frigg-cli/db-setup-command/index.js +0 -193
  166. package/frigg-cli/deploy-command/index.js +0 -175
  167. package/frigg-cli/generate-command/__tests__/generate-command.test.js +0 -301
  168. package/frigg-cli/generate-command/azure-generator.js +0 -43
  169. package/frigg-cli/generate-command/gcp-generator.js +0 -47
  170. package/frigg-cli/generate-command/index.js +0 -332
  171. package/frigg-cli/generate-command/terraform-generator.js +0 -555
  172. package/frigg-cli/generate-iam-command.js +0 -118
  173. package/frigg-cli/index.js +0 -75
  174. package/frigg-cli/index.test.js +0 -158
  175. package/frigg-cli/init-command/backend-first-handler.js +0 -756
  176. package/frigg-cli/init-command/index.js +0 -93
  177. package/frigg-cli/init-command/template-handler.js +0 -143
  178. package/frigg-cli/install-command/backend-js.js +0 -33
  179. package/frigg-cli/install-command/commit-changes.js +0 -16
  180. package/frigg-cli/install-command/environment-variables.js +0 -127
  181. package/frigg-cli/install-command/environment-variables.test.js +0 -136
  182. package/frigg-cli/install-command/index.js +0 -54
  183. package/frigg-cli/install-command/install-package.js +0 -13
  184. package/frigg-cli/install-command/integration-file.js +0 -30
  185. package/frigg-cli/install-command/logger.js +0 -12
  186. package/frigg-cli/install-command/template.js +0 -90
  187. package/frigg-cli/install-command/validate-package.js +0 -75
  188. package/frigg-cli/jest.config.js +0 -124
  189. package/frigg-cli/package.json +0 -54
  190. package/frigg-cli/start-command/index.js +0 -149
  191. package/frigg-cli/start-command/start-command.test.js +0 -297
  192. package/frigg-cli/test/init-command.test.js +0 -180
  193. package/frigg-cli/test/npm-registry.test.js +0 -319
  194. package/frigg-cli/ui-command/index.js +0 -154
  195. package/frigg-cli/utils/app-resolver.js +0 -319
  196. package/frigg-cli/utils/backend-path.js +0 -25
  197. package/frigg-cli/utils/database-validator.js +0 -161
  198. package/frigg-cli/utils/error-messages.js +0 -257
  199. package/frigg-cli/utils/npm-registry.js +0 -167
  200. package/frigg-cli/utils/prisma-runner.js +0 -280
  201. package/frigg-cli/utils/process-manager.js +0 -199
  202. package/frigg-cli/utils/repo-detection.js +0 -405
  203. package/infrastructure/aws-discovery.js +0 -1176
  204. package/infrastructure/aws-discovery.test.js +0 -1220
  205. package/infrastructure/serverless-template.js +0 -2094
  206. /package/infrastructure/{WEBSOCKET-CONFIGURATION.md → docs/WEBSOCKET-CONFIGURATION.md} +0 -0
  207. /package/infrastructure/{GENERATE-IAM-DOCS.md → docs/generate-iam-command.md} +0 -0
  208. /package/infrastructure/{iam-generator.test.js → domains/security/iam-generator.test.js} +0 -0
  209. /package/infrastructure/{frigg-deployment-iam-stack.yaml → domains/security/templates/frigg-deployment-iam-stack.yaml} +0 -0
  210. /package/infrastructure/{iam-policy-basic.json → domains/security/templates/iam-policy-basic.json} +0 -0
  211. /package/infrastructure/{iam-policy-full.json → domains/security/templates/iam-policy-full.json} +0 -0
  212. /package/infrastructure/{run-discovery.js → scripts/run-discovery.js} +0 -0
@@ -1,400 +0,0 @@
1
- /**
2
- * Test suite for install command
3
- *
4
- * Tests the ACTUAL Frigg implementation including:
5
- * - Package search and selection (mocked - external npm)
6
- * - Package installation via npm (mocked - external)
7
- * - Integration file creation (REAL - tests actual file generation)
8
- * - Backend.js updates (REAL - tests actual file parsing/updating)
9
- * - Git commits (mocked - external)
10
- * - Environment variable handling (mocked - interactive)
11
- * - Label sanitization (REAL - tests actual regex logic)
12
- */
13
-
14
- // Mock ONLY external boundaries - let Frigg logic run!
15
- jest.mock('fs-extra'); // Mock at I/O level
16
- jest.mock('../../../install-command/install-package', () => ({
17
- installPackage: jest.fn() // External: npm install
18
- }));
19
- jest.mock('../../../install-command/commit-changes', () => ({
20
- commitChanges: jest.fn() // External: git commands
21
- }));
22
- jest.mock('../../../install-command/environment-variables', () => ({
23
- handleEnvVariables: jest.fn() // External: interactive prompts
24
- }));
25
- jest.mock('../../../install-command/validate-package', () => ({
26
- validatePackageExists: jest.fn(), // External: npm registry
27
- searchAndSelectPackage: jest.fn() // External: interactive selection
28
- }));
29
- jest.mock('@friggframework/core', () => ({
30
- findNearestBackendPackageJson: jest.fn(),
31
- validateBackendPath: jest.fn()
32
- }));
33
-
34
- // DON'T mock these - let them run to test actual Frigg logic:
35
- // - createIntegrationFile (tests file generation)
36
- // - updateBackendJsFile (tests file parsing)
37
- // - logger (just console.log, we'll spy on console)
38
- // - getIntegrationTemplate (tests template generation)
39
-
40
- // Require after mocks
41
- const fs = require('fs-extra');
42
- const { installPackage } = require('../../../install-command/install-package');
43
- const { commitChanges } = require('../../../install-command/commit-changes');
44
- const { handleEnvVariables } = require('../../../install-command/environment-variables');
45
- const { validatePackageExists, searchAndSelectPackage } = require('../../../install-command/validate-package');
46
- const { findNearestBackendPackageJson, validateBackendPath } = require('@friggframework/core');
47
- const { installCommand } = require('../../../install-command');
48
-
49
- describe('CLI Command: install', () => {
50
- let processExitSpy;
51
- let consoleLogSpy;
52
- let consoleErrorSpy;
53
- const mockBackendPath = '/mock/backend/package.json';
54
- const mockBackendDir = '/mock/backend';
55
-
56
- beforeEach(() => {
57
- jest.clearAllMocks();
58
-
59
- // Mock process.exit to prevent actual exit
60
- processExitSpy = jest.spyOn(process, 'exit').mockImplementation();
61
-
62
- // Spy on console for logger (don't mock logger - test it!)
63
- consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
64
- consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
65
-
66
- // Setup fs-extra mocks - Let Frigg code run, just mock I/O
67
- fs.ensureDirSync = jest.fn();
68
- fs.writeFileSync = jest.fn();
69
- fs.readFileSync = jest.fn().mockReturnValue(`
70
- // Sample backend.js file
71
- const integrations = [
72
- // Existing integrations
73
- ];
74
-
75
- module.exports = {
76
- integrations: []
77
- };
78
- `);
79
- fs.existsSync = jest.fn().mockReturnValue(true);
80
-
81
- // Setup default successful mocks for external boundaries
82
- searchAndSelectPackage.mockResolvedValue(['@friggframework/api-module-slack']);
83
- findNearestBackendPackageJson.mockReturnValue(mockBackendPath);
84
- validateBackendPath.mockReturnValue(true);
85
- validatePackageExists.mockResolvedValue(true);
86
- installPackage.mockReturnValue(undefined);
87
- handleEnvVariables.mockResolvedValue(undefined);
88
-
89
- // Mock the dynamic require() of installed package using jest.doMock
90
- const path = require('path');
91
- const slackModulePath = path.resolve(mockBackendPath, '../../node_modules/@friggframework/api-module-slack');
92
-
93
- jest.doMock(slackModulePath, () => ({
94
- Config: { label: 'Slack' },
95
- Api: class SlackApi {}
96
- }), { virtual: true });
97
- });
98
-
99
- afterEach(() => {
100
- processExitSpy.mockRestore();
101
- consoleLogSpy.mockRestore();
102
- consoleErrorSpy.mockRestore();
103
- jest.resetModules(); // Clear module cache after each test
104
- });
105
-
106
- describe('Success Cases', () => {
107
- it('should orchestrate complete installation workflow', async () => {
108
- await installCommand('slack');
109
-
110
- // Verify external boundaries called
111
- expect(searchAndSelectPackage).toHaveBeenCalledWith('slack');
112
- expect(findNearestBackendPackageJson).toHaveBeenCalled();
113
- expect(validateBackendPath).toHaveBeenCalledWith(mockBackendPath);
114
- expect(validatePackageExists).toHaveBeenCalledWith('@friggframework/api-module-slack');
115
- expect(installPackage).toHaveBeenCalledWith(mockBackendPath, '@friggframework/api-module-slack');
116
- });
117
-
118
- it('should create integration file with correct path and content', async () => {
119
- await installCommand('slack');
120
-
121
- // Verify directory creation
122
- expect(fs.ensureDirSync).toHaveBeenCalledWith(
123
- expect.stringMatching(/src\/integrations$/)
124
- );
125
-
126
- // Verify integration file written with correct path
127
- expect(fs.writeFileSync).toHaveBeenCalledWith(
128
- expect.stringMatching(/SlackIntegration\.js$/),
129
- expect.any(String)
130
- );
131
-
132
- // Get the actual content that was written
133
- const writeCall = fs.writeFileSync.mock.calls.find(call =>
134
- call[0].includes('SlackIntegration.js')
135
- );
136
-
137
- expect(writeCall).toBeDefined();
138
- const [filePath, content] = writeCall;
139
-
140
- // Verify file content contains valid integration class
141
- expect(content).toContain('class SlackIntegration extends IntegrationBase');
142
- expect(content).toContain('@friggframework/core');
143
- expect(content).toContain('@friggframework/api-module-slack');
144
- });
145
-
146
- it('should generate valid JavaScript template', async () => {
147
- await installCommand('slack');
148
-
149
- const writeCall = fs.writeFileSync.mock.calls.find(call =>
150
- call[0].includes('SlackIntegration.js')
151
- );
152
-
153
- const [, content] = writeCall;
154
-
155
- // Verify template has required structure
156
- expect(content).toMatch(/class \w+Integration extends IntegrationBase/);
157
- expect(content).toContain('static Config =');
158
- expect(content).toContain('static Options =');
159
- expect(content).toContain('static modules =');
160
-
161
- // Verify template is syntactically valid (no unclosed braces, etc)
162
- expect(content.split('{').length).toBe(content.split('}').length);
163
- });
164
-
165
- it('should update backend.js with integration import', async () => {
166
- await installCommand('slack');
167
-
168
- // Verify backend.js was read
169
- expect(fs.readFileSync).toHaveBeenCalledWith(
170
- expect.stringMatching(/backend\.js$/),
171
- 'utf-8'
172
- );
173
-
174
- // Verify backend.js was written back with import
175
- const backendWriteCall = fs.writeFileSync.mock.calls.find(call =>
176
- call[0].includes('backend.js')
177
- );
178
-
179
- expect(backendWriteCall).toBeDefined();
180
- const [, updatedBackend] = backendWriteCall;
181
-
182
- // Verify import statement added
183
- expect(updatedBackend).toContain('const SlackIntegration = require');
184
- expect(updatedBackend).toContain('./src/integrations/SlackIntegration');
185
-
186
- // Verify integration added to array
187
- expect(updatedBackend).toContain('SlackIntegration,');
188
- });
189
-
190
- it('should commit changes after file operations', async () => {
191
- await installCommand('slack');
192
-
193
- expect(commitChanges).toHaveBeenCalledWith(mockBackendPath, 'Slack');
194
- });
195
-
196
- it('should handle environment variables after installation', async () => {
197
- await installCommand('slack');
198
-
199
- expect(handleEnvVariables).toHaveBeenCalledWith(
200
- mockBackendPath,
201
- expect.stringContaining('@friggframework/api-module-slack')
202
- );
203
- });
204
-
205
- it('should log info messages during installation', async () => {
206
- await installCommand('slack');
207
-
208
- // Verify logger actually logged (we spy on console)
209
- expect(consoleLogSpy).toHaveBeenCalledWith(
210
- expect.stringContaining('Successfully installed @friggframework/api-module-slack')
211
- );
212
- });
213
-
214
- it('should install multiple packages sequentially', async () => {
215
- searchAndSelectPackage.mockResolvedValue([
216
- '@friggframework/api-module-slack',
217
- '@friggframework/api-module-hubspot'
218
- ]);
219
-
220
- // Mock HubSpot module (Slack already mocked in beforeEach)
221
- const path = require('path');
222
- const hubspotPath = path.resolve(mockBackendPath, '../../node_modules/@friggframework/api-module-hubspot');
223
-
224
- jest.doMock(hubspotPath, () => ({
225
- Config: { label: 'HubSpot' },
226
- Api: class HubSpotApi {}
227
- }), { virtual: true });
228
-
229
- await installCommand('crm');
230
-
231
- expect(validatePackageExists).toHaveBeenCalledTimes(2);
232
- expect(installPackage).toHaveBeenCalledTimes(2);
233
-
234
- // Verify TWO integration files created
235
- const integrationFiles = fs.writeFileSync.mock.calls.filter(call =>
236
- call[0].includes('Integration.js') && !call[0].includes('backend.js')
237
- );
238
- expect(integrationFiles.length).toBe(2);
239
-
240
- // Verify both files have correct names
241
- expect(integrationFiles[0][0]).toContain('SlackIntegration.js');
242
- expect(integrationFiles[1][0]).toContain('HubSpotIntegration.js');
243
- });
244
-
245
- it('should sanitize label by removing invalid characters', async () => {
246
- // Mock different package with special characters in label
247
- searchAndSelectPackage.mockResolvedValue(['@friggframework/api-module-google-drive']);
248
-
249
- const path = require('path');
250
- const googleDrivePath = path.resolve(mockBackendPath, '../../node_modules/@friggframework/api-module-google-drive');
251
-
252
- jest.doMock(googleDrivePath, () => ({
253
- Config: { label: 'Google<Drive>' }, // Has invalid characters
254
- Api: class GoogleDriveApi {}
255
- }), { virtual: true });
256
-
257
- await installCommand('google-drive');
258
-
259
- // Verify sanitized label used in file name
260
- const writeCall = fs.writeFileSync.mock.calls.find(call =>
261
- call[0].includes('Integration.js')
262
- );
263
-
264
- // Should be GoogleDrive, not Google<Drive>
265
- expect(writeCall[0]).toContain('GoogleDriveIntegration.js');
266
- expect(writeCall[0]).not.toContain('<');
267
- expect(writeCall[0]).not.toContain('>');
268
-
269
- // Verify content uses sanitized name
270
- expect(writeCall[1]).toContain('class GoogleDriveIntegration');
271
- expect(writeCall[1]).not.toContain('Google<Drive>');
272
- });
273
-
274
- it('should sanitize label by removing spaces', async () => {
275
- // Mock different package with spaces in label
276
- searchAndSelectPackage.mockResolvedValue(['@friggframework/api-module-google-calendar']);
277
-
278
- const path = require('path');
279
- const googleCalendarPath = path.resolve(mockBackendPath, '../../node_modules/@friggframework/api-module-google-calendar');
280
-
281
- jest.doMock(googleCalendarPath, () => ({
282
- Config: { label: 'Google Calendar' }, // Has spaces
283
- Api: class GoogleCalendarApi {}
284
- }), { virtual: true });
285
-
286
- await installCommand('google-calendar');
287
-
288
- // Verify sanitized label used in file name (no spaces)
289
- const writeCall = fs.writeFileSync.mock.calls.find(call =>
290
- call[0].includes('Integration.js')
291
- );
292
-
293
- expect(writeCall[0]).toContain('GoogleCalendarIntegration.js');
294
- expect(writeCall[0]).not.toContain(' ');
295
-
296
- // Verify content uses sanitized name
297
- expect(writeCall[1]).toContain('class GoogleCalendarIntegration');
298
- expect(writeCall[1]).not.toMatch(/class Google Calendar/);
299
- });
300
- });
301
-
302
- describe('Early Exit Cases', () => {
303
- it('should return early when no packages selected', async () => {
304
- searchAndSelectPackage.mockResolvedValue([]);
305
-
306
- await installCommand('slack');
307
-
308
- expect(findNearestBackendPackageJson).not.toHaveBeenCalled();
309
- expect(validatePackageExists).not.toHaveBeenCalled();
310
- expect(installPackage).not.toHaveBeenCalled();
311
- });
312
-
313
- it('should return early when packages is null', async () => {
314
- searchAndSelectPackage.mockResolvedValue(null);
315
-
316
- await installCommand('slack');
317
-
318
- expect(findNearestBackendPackageJson).not.toHaveBeenCalled();
319
- });
320
-
321
- it('should return early when packages is undefined', async () => {
322
- searchAndSelectPackage.mockResolvedValue(undefined);
323
-
324
- await installCommand('slack');
325
-
326
- expect(findNearestBackendPackageJson).not.toHaveBeenCalled();
327
- });
328
- });
329
-
330
- describe('Error Handling', () => {
331
- it('should log error and exit on searchAndSelectPackage failure', async () => {
332
- const error = new Error('Search failed');
333
- searchAndSelectPackage.mockRejectedValue(error);
334
-
335
- await installCommand('slack');
336
-
337
- // Verify error logged via console.error (we spy on it)
338
- expect(consoleErrorSpy).toHaveBeenCalledWith('An error occurred:', error);
339
- expect(processExitSpy).toHaveBeenCalledWith(1);
340
- });
341
-
342
- it('should log error and exit on validatePackageExists failure', async () => {
343
- const error = new Error('Package not found');
344
- validatePackageExists.mockRejectedValue(error);
345
-
346
- await installCommand('slack');
347
-
348
- expect(consoleErrorSpy).toHaveBeenCalledWith('An error occurred:', error);
349
- expect(processExitSpy).toHaveBeenCalledWith(1);
350
- });
351
-
352
- it('should log error and exit on installPackage failure', async () => {
353
- const error = new Error('Installation failed');
354
- installPackage.mockImplementation(() => {
355
- throw error;
356
- });
357
-
358
- await installCommand('slack');
359
-
360
- expect(consoleErrorSpy).toHaveBeenCalledWith('An error occurred:', error);
361
- expect(processExitSpy).toHaveBeenCalledWith(1);
362
- });
363
-
364
- it('should log error and exit on file write failure (createIntegrationFile)', async () => {
365
- // Make fs.writeFileSync throw - tests REAL error path
366
- const error = new Error('EACCES: permission denied');
367
- fs.writeFileSync.mockImplementation(() => {
368
- throw error;
369
- });
370
-
371
- await installCommand('slack');
372
-
373
- expect(consoleErrorSpy).toHaveBeenCalledWith('An error occurred:', expect.any(Error));
374
- expect(processExitSpy).toHaveBeenCalledWith(1);
375
- });
376
-
377
- it('should log error and exit on backend.js read failure', async () => {
378
- // Make fs.readFileSync throw - tests REAL error path
379
- const error = new Error('ENOENT: file not found');
380
- fs.readFileSync.mockImplementation(() => {
381
- throw error;
382
- });
383
-
384
- await installCommand('slack');
385
-
386
- expect(consoleErrorSpy).toHaveBeenCalledWith('An error occurred:', expect.any(Error));
387
- expect(processExitSpy).toHaveBeenCalledWith(1);
388
- });
389
-
390
- it('should log error and exit on handleEnvVariables failure', async () => {
391
- const error = new Error('Env variables failed');
392
- handleEnvVariables.mockRejectedValue(error);
393
-
394
- await installCommand('slack');
395
-
396
- expect(consoleErrorSpy).toHaveBeenCalledWith('An error occurred:', error);
397
- expect(processExitSpy).toHaveBeenCalledWith(1);
398
- });
399
- });
400
- });