@friggframework/devtools 2.0.0--canary.474.2ad4ebc.0 → 2.0.0--canary.474.a0b734c.0

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 (56) hide show
  1. package/management-ui/server/utils/cliIntegration.js +1 -1
  2. package/package.json +6 -6
  3. package/frigg-cli/.eslintrc.js +0 -141
  4. package/frigg-cli/README.md +0 -1290
  5. package/frigg-cli/__tests__/unit/commands/build.test.js +0 -279
  6. package/frigg-cli/__tests__/unit/commands/db-setup.test.js +0 -548
  7. package/frigg-cli/__tests__/unit/commands/deploy.test.js +0 -320
  8. package/frigg-cli/__tests__/unit/commands/install.test.js +0 -400
  9. package/frigg-cli/__tests__/unit/commands/ui.test.js +0 -346
  10. package/frigg-cli/__tests__/unit/utils/database-validator.test.js +0 -366
  11. package/frigg-cli/__tests__/unit/utils/error-messages.test.js +0 -304
  12. package/frigg-cli/__tests__/unit/version-detection.test.js +0 -171
  13. package/frigg-cli/__tests__/utils/mock-factory.js +0 -270
  14. package/frigg-cli/__tests__/utils/prisma-mock.js +0 -194
  15. package/frigg-cli/__tests__/utils/test-fixtures.js +0 -463
  16. package/frigg-cli/__tests__/utils/test-setup.js +0 -287
  17. package/frigg-cli/build-command/index.js +0 -66
  18. package/frigg-cli/db-setup-command/index.js +0 -193
  19. package/frigg-cli/deploy-command/index.js +0 -302
  20. package/frigg-cli/doctor-command/index.js +0 -249
  21. package/frigg-cli/generate-command/__tests__/generate-command.test.js +0 -301
  22. package/frigg-cli/generate-command/azure-generator.js +0 -43
  23. package/frigg-cli/generate-command/gcp-generator.js +0 -47
  24. package/frigg-cli/generate-command/index.js +0 -332
  25. package/frigg-cli/generate-command/terraform-generator.js +0 -555
  26. package/frigg-cli/generate-iam-command.js +0 -118
  27. package/frigg-cli/index.js +0 -173
  28. package/frigg-cli/index.test.js +0 -158
  29. package/frigg-cli/init-command/backend-first-handler.js +0 -756
  30. package/frigg-cli/init-command/index.js +0 -93
  31. package/frigg-cli/init-command/template-handler.js +0 -143
  32. package/frigg-cli/install-command/backend-js.js +0 -33
  33. package/frigg-cli/install-command/commit-changes.js +0 -16
  34. package/frigg-cli/install-command/environment-variables.js +0 -127
  35. package/frigg-cli/install-command/environment-variables.test.js +0 -136
  36. package/frigg-cli/install-command/index.js +0 -54
  37. package/frigg-cli/install-command/install-package.js +0 -13
  38. package/frigg-cli/install-command/integration-file.js +0 -30
  39. package/frigg-cli/install-command/logger.js +0 -12
  40. package/frigg-cli/install-command/template.js +0 -90
  41. package/frigg-cli/install-command/validate-package.js +0 -75
  42. package/frigg-cli/jest.config.js +0 -124
  43. package/frigg-cli/package.json +0 -58
  44. package/frigg-cli/repair-command/index.js +0 -341
  45. package/frigg-cli/start-command/index.js +0 -149
  46. package/frigg-cli/start-command/start-command.test.js +0 -297
  47. package/frigg-cli/test/init-command.test.js +0 -180
  48. package/frigg-cli/test/npm-registry.test.js +0 -319
  49. package/frigg-cli/ui-command/index.js +0 -154
  50. package/frigg-cli/utils/app-resolver.js +0 -319
  51. package/frigg-cli/utils/backend-path.js +0 -25
  52. package/frigg-cli/utils/database-validator.js +0 -154
  53. package/frigg-cli/utils/error-messages.js +0 -257
  54. package/frigg-cli/utils/npm-registry.js +0 -167
  55. package/frigg-cli/utils/process-manager.js +0 -199
  56. package/frigg-cli/utils/repo-detection.js +0 -405
@@ -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
- });