@friggframework/devtools 2.0.0--canary.522.923dfae.0 → 2.0.0--canary.540.c5ef83f.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 (119) hide show
  1. package/frigg-cli/README.md +1 -1
  2. package/frigg-cli/__tests__/unit/commands/doctor.test.js +2 -0
  3. package/frigg-cli/__tests__/unit/commands/install.test.js +19 -23
  4. package/frigg-cli/__tests__/unit/dependencies.test.js +2 -2
  5. package/frigg-cli/doctor-command/index.js +16 -17
  6. package/frigg-cli/index.js +6 -21
  7. package/frigg-cli/index.test.js +2 -7
  8. package/frigg-cli/init-command/backend-first-handler.js +42 -124
  9. package/frigg-cli/init-command/index.js +1 -2
  10. package/frigg-cli/init-command/template-handler.js +3 -13
  11. package/frigg-cli/install-command/backend-js.js +3 -3
  12. package/frigg-cli/install-command/environment-variables.js +19 -16
  13. package/frigg-cli/install-command/environment-variables.test.js +13 -12
  14. package/frigg-cli/install-command/index.js +9 -14
  15. package/frigg-cli/install-command/integration-file.js +3 -3
  16. package/frigg-cli/install-command/logger.js +12 -0
  17. package/frigg-cli/install-command/validate-package.js +9 -5
  18. package/frigg-cli/jest.config.js +1 -4
  19. package/frigg-cli/repair-command/index.js +128 -101
  20. package/frigg-cli/start-command/index.js +2 -246
  21. package/frigg-cli/ui-command/index.js +36 -58
  22. package/frigg-cli/utils/repo-detection.js +37 -85
  23. package/infrastructure/docs/iam-policy-templates.md +1 -1
  24. package/infrastructure/domains/networking/vpc-builder.test.js +4 -2
  25. package/infrastructure/domains/networking/vpc-resolver.test.js +1 -1
  26. package/infrastructure/domains/shared/cloudformation-discovery.test.js +7 -4
  27. package/infrastructure/domains/shared/resource-discovery.js +5 -5
  28. package/infrastructure/domains/shared/types/app-definition.js +0 -21
  29. package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
  30. package/infrastructure/domains/shared/utilities/base-definition-factory.js +1 -10
  31. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
  32. package/infrastructure/infrastructure-composer.test.js +2 -2
  33. package/management-ui/README.md +109 -245
  34. package/package.json +7 -8
  35. package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +0 -326
  36. package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +0 -337
  37. package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +0 -373
  38. package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +0 -313
  39. package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +0 -269
  40. package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +0 -82
  41. package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +0 -408
  42. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +0 -583
  43. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +0 -314
  44. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +0 -383
  45. package/frigg-cli/__tests__/unit/commands/init.test.js +0 -406
  46. package/frigg-cli/__tests__/unit/commands/repair.test.js +0 -275
  47. package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +0 -411
  48. package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +0 -405
  49. package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +0 -496
  50. package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +0 -474
  51. package/frigg-cli/__tests__/unit/utils/output.test.js +0 -196
  52. package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +0 -93
  53. package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +0 -93
  54. package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +0 -103
  55. package/frigg-cli/container.js +0 -172
  56. package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +0 -286
  57. package/frigg-cli/domain/entities/ApiModule.js +0 -272
  58. package/frigg-cli/domain/entities/AppDefinition.js +0 -227
  59. package/frigg-cli/domain/entities/Integration.js +0 -198
  60. package/frigg-cli/domain/exceptions/DomainException.js +0 -24
  61. package/frigg-cli/domain/ports/IApiModuleRepository.js +0 -53
  62. package/frigg-cli/domain/ports/IAppDefinitionRepository.js +0 -43
  63. package/frigg-cli/domain/ports/IIntegrationRepository.js +0 -61
  64. package/frigg-cli/domain/services/IntegrationValidator.js +0 -185
  65. package/frigg-cli/domain/value-objects/IntegrationId.js +0 -42
  66. package/frigg-cli/domain/value-objects/IntegrationName.js +0 -60
  67. package/frigg-cli/domain/value-objects/SemanticVersion.js +0 -70
  68. package/frigg-cli/infrastructure/UnitOfWork.js +0 -46
  69. package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +0 -197
  70. package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +0 -224
  71. package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +0 -249
  72. package/frigg-cli/infrastructure/adapters/SchemaValidator.js +0 -92
  73. package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +0 -373
  74. package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +0 -116
  75. package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +0 -277
  76. package/frigg-cli/package-lock.json +0 -16226
  77. package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +0 -376
  78. package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +0 -591
  79. package/frigg-cli/start-command/infrastructure/DockerAdapter.js +0 -306
  80. package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +0 -329
  81. package/frigg-cli/templates/backend/.env.example +0 -62
  82. package/frigg-cli/templates/backend/.eslintrc.json +0 -12
  83. package/frigg-cli/templates/backend/.prettierrc +0 -6
  84. package/frigg-cli/templates/backend/docker-compose.yml +0 -22
  85. package/frigg-cli/templates/backend/index.js +0 -96
  86. package/frigg-cli/templates/backend/infrastructure.js +0 -12
  87. package/frigg-cli/templates/backend/jest.config.js +0 -17
  88. package/frigg-cli/templates/backend/package.json +0 -50
  89. package/frigg-cli/templates/backend/src/api-modules/.gitkeep +0 -10
  90. package/frigg-cli/templates/backend/src/base/.gitkeep +0 -7
  91. package/frigg-cli/templates/backend/src/integrations/.gitkeep +0 -10
  92. package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +0 -65
  93. package/frigg-cli/templates/backend/src/utils/.gitkeep +0 -7
  94. package/frigg-cli/templates/backend/test/setup.js +0 -30
  95. package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
  96. package/frigg-cli/templates/backend/ui-extensions/README.md +0 -77
  97. package/frigg-cli/utils/__tests__/repo-detection.test.js +0 -436
  98. package/frigg-cli/utils/output.js +0 -382
  99. package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +0 -205
  100. package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +0 -104
  101. package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +0 -153
  102. package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +0 -162
  103. package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +0 -152
  104. package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +0 -332
  105. package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +0 -191
  106. package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +0 -146
  107. package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +0 -155
  108. package/frigg-cli/validate-command/adapters/cli/validate-command.js +0 -199
  109. package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +0 -35
  110. package/frigg-cli/validate-command/domain/entities/validation-result.js +0 -74
  111. package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +0 -74
  112. package/frigg-cli/validate-command/domain/value-objects/validation-error.js +0 -68
  113. package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +0 -181
  114. package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +0 -128
  115. package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +0 -113
  116. package/infrastructure/domains/admin-scripts/admin-script-builder.js +0 -200
  117. package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +0 -499
  118. package/infrastructure/domains/admin-scripts/index.js +0 -5
  119. package/infrastructure/jest.config.js +0 -16
@@ -1,382 +0,0 @@
1
- /**
2
- * Output - Unified CLI output and interaction utilities
3
- *
4
- * Provides consistent formatting, colors, and interactive prompts across all CLI commands.
5
- * Replaces direct usage of chalk, console.log, inquirer, and readline.
6
- *
7
- * @example
8
- * const output = require('./utils/output');
9
- *
10
- * output.success('Module installed successfully');
11
- * output.error('Failed to connect to database', error);
12
- * output.info('Starting deployment...');
13
- *
14
- * const spinner = output.spinner('Downloading dependencies...');
15
- * // do work
16
- * spinner.succeed('Dependencies downloaded');
17
- *
18
- * const answer = await output.prompt({
19
- * type: 'confirm',
20
- * name: 'continue',
21
- * message: 'Continue with installation?'
22
- * });
23
- */
24
-
25
- const chalk = require('chalk');
26
- const { select, input, confirm, checkbox, password } = require('@inquirer/prompts');
27
-
28
- class Output {
29
- /**
30
- * Display a success message with a checkmark
31
- * @param {string} message - Success message to display
32
- */
33
- success(message) {
34
- console.log(chalk.green('✓'), chalk.green(message));
35
- }
36
-
37
- /**
38
- * Display an error message with an X mark
39
- * @param {string} message - Error message to display
40
- * @param {Error} [error] - Optional error object to display
41
- */
42
- error(message, error) {
43
- console.error(chalk.red('✗'), chalk.red(message));
44
- if (error && process.env.DEBUG) {
45
- console.error(chalk.gray(error.stack || error.message));
46
- }
47
- }
48
-
49
- /**
50
- * Display an info message with an info icon
51
- * @param {string} message - Info message to display
52
- */
53
- info(message) {
54
- console.log(chalk.blue('ℹ'), message);
55
- }
56
-
57
- /**
58
- * Display a warning message
59
- * @param {string} message - Warning message to display
60
- */
61
- warn(message) {
62
- console.warn(chalk.yellow('⚠'), chalk.yellow(message));
63
- }
64
-
65
- /**
66
- * Display a debug message (only when DEBUG env var is set)
67
- * @param {string} message - Debug message to display
68
- */
69
- debug(message) {
70
- if (process.env.DEBUG) {
71
- console.log(chalk.gray('🐛'), chalk.gray(message));
72
- }
73
- }
74
-
75
- /**
76
- * Display a header/title
77
- * @param {string} title - Title to display
78
- */
79
- header(title) {
80
- console.log('');
81
- console.log(chalk.bold.cyan(title));
82
- console.log(chalk.cyan('─'.repeat(title.length)));
83
- }
84
-
85
- /**
86
- * Display a section separator
87
- */
88
- separator() {
89
- console.log(chalk.gray('─'.repeat(50)));
90
- }
91
-
92
- /**
93
- * Display a blank line
94
- */
95
- newline() {
96
- console.log('');
97
- }
98
-
99
- /**
100
- * Display a table of data
101
- * @param {Array<Object>} data - Array of objects to display
102
- * @param {Array<string>} [columns] - Column keys to display (defaults to all)
103
- */
104
- table(data, columns) {
105
- if (!data || data.length === 0) {
106
- this.info('No data to display');
107
- return;
108
- }
109
-
110
- const keys = columns || Object.keys(data[0]);
111
-
112
- // Calculate column widths
113
- const widths = {};
114
- keys.forEach(key => {
115
- widths[key] = Math.max(
116
- key.length,
117
- ...data.map(row => String(row[key] || '').length)
118
- );
119
- });
120
-
121
- // Print header
122
- const headerRow = keys.map(key =>
123
- chalk.bold(key.padEnd(widths[key]))
124
- ).join(' ');
125
- console.log(headerRow);
126
- console.log(keys.map(key =>
127
- '─'.repeat(widths[key])
128
- ).join(' '));
129
-
130
- // Print rows
131
- data.forEach(row => {
132
- const dataRow = keys.map(key =>
133
- String(row[key] || '').padEnd(widths[key])
134
- ).join(' ');
135
- console.log(dataRow);
136
- });
137
- }
138
-
139
- /**
140
- * Display key-value pairs
141
- * @param {Object} data - Object with key-value pairs
142
- */
143
- keyValue(data) {
144
- const maxKeyLength = Math.max(...Object.keys(data).map(k => k.length));
145
-
146
- Object.entries(data).forEach(([key, value]) => {
147
- const formattedKey = chalk.gray(`${key.padEnd(maxKeyLength)}:`);
148
- console.log(`${formattedKey} ${value}`);
149
- });
150
- }
151
-
152
- /**
153
- * Display JSON with syntax highlighting
154
- * @param {Object} data - Data to display as JSON
155
- * @param {number} [indent=2] - Indentation level
156
- */
157
- json(data, indent = 2) {
158
- const json = JSON.stringify(data, null, indent);
159
- // Basic syntax highlighting
160
- const highlighted = json
161
- .replace(/"([^"]+)":/g, chalk.blue('"$1"') + ':') // keys
162
- .replace(/: "([^"]+)"/g, ': ' + chalk.green('"$1"')) // string values
163
- .replace(/: (\d+)/g, ': ' + chalk.yellow('$1')) // numbers
164
- .replace(/: (true|false|null)/g, ': ' + chalk.magenta('$1')); // booleans/null
165
-
166
- console.log(highlighted);
167
- }
168
-
169
- /**
170
- * Create a spinner for long-running operations
171
- * @param {string} text - Spinner text
172
- * @returns {Object} Spinner object with update/succeed/fail/stop methods
173
- */
174
- spinner(text) {
175
- const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
176
- let frameIndex = 0;
177
- let interval = null;
178
- let currentText = text;
179
-
180
- const render = () => {
181
- process.stdout.write(`\r${chalk.cyan(frames[frameIndex])} ${currentText}`);
182
- frameIndex = (frameIndex + 1) % frames.length;
183
- };
184
-
185
- const clear = () => {
186
- process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r');
187
- };
188
-
189
- const start = () => {
190
- if (interval) return;
191
- interval = setInterval(render, 80);
192
- };
193
-
194
- const stop = () => {
195
- if (interval) {
196
- clearInterval(interval);
197
- interval = null;
198
- clear();
199
- }
200
- };
201
-
202
- // Auto-start
203
- start();
204
-
205
- return {
206
- update: (newText) => {
207
- currentText = newText;
208
- },
209
- succeed: (message) => {
210
- stop();
211
- this.success(message || currentText);
212
- },
213
- fail: (message) => {
214
- stop();
215
- this.error(message || currentText);
216
- },
217
- stop: () => {
218
- stop();
219
- }
220
- };
221
- }
222
-
223
- /**
224
- * Prompt user with a question (uses @inquirer/prompts)
225
- * @param {Object} question - Question configuration
226
- * @returns {Promise<any>} User's answer
227
- */
228
- async prompt(question) {
229
- const { type, name, message, choices, initial, validate } = question;
230
-
231
- try {
232
- switch (type) {
233
- case 'select':
234
- return await select({ message, choices, default: initial });
235
-
236
- case 'input':
237
- return await input({ message, default: initial, validate });
238
-
239
- case 'confirm':
240
- return await confirm({ message, default: initial });
241
-
242
- case 'checkbox':
243
- return await checkbox({ message, choices, validate });
244
-
245
- case 'password':
246
- return await password({ message, validate });
247
-
248
- default:
249
- throw new Error(`Unknown prompt type: ${type}`);
250
- }
251
- } catch (error) {
252
- // User cancelled (Ctrl+C)
253
- if (error.message === 'User force closed the prompt') {
254
- this.warn('Operation cancelled by user');
255
- process.exit(0);
256
- }
257
- throw error;
258
- }
259
- }
260
-
261
- /**
262
- * Prompt for confirmation
263
- * @param {string} message - Confirmation message
264
- * @param {boolean} [defaultValue=false] - Default value
265
- * @returns {Promise<boolean>} User's answer
266
- */
267
- async confirm(message, defaultValue = false) {
268
- return this.prompt({
269
- type: 'confirm',
270
- message,
271
- initial: defaultValue
272
- });
273
- }
274
-
275
- /**
276
- * Prompt for text input
277
- * @param {string} message - Input message
278
- * @param {string} [defaultValue] - Default value
279
- * @param {Function} [validate] - Validation function
280
- * @returns {Promise<string>} User's input
281
- */
282
- async input(message, defaultValue, validate) {
283
- return this.prompt({
284
- type: 'input',
285
- message,
286
- initial: defaultValue,
287
- validate
288
- });
289
- }
290
-
291
- /**
292
- * Prompt for selection from a list
293
- * @param {string} message - Selection message
294
- * @param {Array<Object|string>} choices - Array of choices
295
- * @param {any} [defaultValue] - Default value
296
- * @returns {Promise<any>} Selected value
297
- */
298
- async select(message, choices, defaultValue) {
299
- // Normalize choices to {name, value} format
300
- const normalizedChoices = choices.map(choice => {
301
- if (typeof choice === 'string') {
302
- return { name: choice, value: choice };
303
- }
304
- return choice;
305
- });
306
-
307
- return this.prompt({
308
- type: 'select',
309
- message,
310
- choices: normalizedChoices,
311
- initial: defaultValue
312
- });
313
- }
314
-
315
- /**
316
- * Prompt for multiple selections
317
- * @param {string} message - Selection message
318
- * @param {Array<Object|string>} choices - Array of choices
319
- * @returns {Promise<Array>} Selected values
320
- */
321
- async checkbox(message, choices) {
322
- // Normalize choices to {name, value, checked} format
323
- const normalizedChoices = choices.map(choice => {
324
- if (typeof choice === 'string') {
325
- return { name: choice, value: choice, checked: false };
326
- }
327
- return { ...choice, checked: choice.checked || false };
328
- });
329
-
330
- return this.prompt({
331
- type: 'checkbox',
332
- message,
333
- choices: normalizedChoices
334
- });
335
- }
336
-
337
- /**
338
- * Prompt for password input
339
- * @param {string} message - Password prompt message
340
- * @param {Function} [validate] - Validation function
341
- * @returns {Promise<string>} User's password
342
- */
343
- async password(message, validate) {
344
- return this.prompt({
345
- type: 'password',
346
- message,
347
- validate
348
- });
349
- }
350
-
351
- /**
352
- * Display a progress bar
353
- * @param {number} current - Current progress (0-100)
354
- * @param {number} total - Total (usually 100)
355
- * @param {string} [message] - Optional message
356
- */
357
- progress(current, total = 100, message = '') {
358
- const percentage = Math.round((current / total) * 100);
359
- const barLength = 40;
360
- const filledLength = Math.round((barLength * current) / total);
361
- const bar = '█'.repeat(filledLength) + '░'.repeat(barLength - filledLength);
362
-
363
- process.stdout.write(
364
- `\r${chalk.cyan(bar)} ${chalk.bold(`${percentage}%`)} ${message}`
365
- );
366
-
367
- if (current >= total) {
368
- console.log(''); // New line when complete
369
- }
370
- }
371
-
372
- /**
373
- * Log raw message without formatting (for compatibility)
374
- * @param {...any} args - Arguments to log
375
- */
376
- log(...args) {
377
- console.log(...args);
378
- }
379
- }
380
-
381
- // Export a singleton instance
382
- module.exports = new Output();
@@ -1,205 +0,0 @@
1
- const path = require('path');
2
- const fs = require('fs');
3
- const os = require('os');
4
- const {
5
- createValidateCommand,
6
- formatConsoleOutput,
7
- autoDetectFriggApp,
8
- findBackendPathInDir,
9
- findBackendPath
10
- } = require('../../adapters/cli/validate-command');
11
- const { ValidationResult } = require('../../domain/entities/validation-result');
12
- const { ValidationError } = require('../../domain/value-objects/validation-error');
13
- const { FixSuggestion } = require('../../domain/value-objects/fix-suggestion');
14
- const { Command } = require('commander');
15
-
16
- describe('validateCommand', () => {
17
- let mockOutput;
18
-
19
- beforeEach(() => {
20
- mockOutput = {
21
- info: jest.fn(),
22
- success: jest.fn(),
23
- error: jest.fn(),
24
- warn: jest.fn(),
25
- log: jest.fn()
26
- };
27
- });
28
-
29
- describe('createValidateCommand', () => {
30
- it('registers validate command on program', () => {
31
- const program = new Command();
32
- createValidateCommand(program);
33
- const validateCmd = program.commands.find(c => c.name() === 'validate');
34
- expect(validateCmd).toBeDefined();
35
- });
36
-
37
- it('has required options', () => {
38
- const program = new Command();
39
- createValidateCommand(program);
40
- const validateCmd = program.commands.find(c => c.name() === 'validate');
41
- const options = validateCmd.options.map(o => o.long);
42
- expect(options).toContain('--format');
43
- expect(options).toContain('--verbose');
44
- });
45
- });
46
-
47
- describe('formatConsoleOutput', () => {
48
- it('outputs success for valid result', () => {
49
- const result = ValidationResult.create();
50
- formatConsoleOutput(result, {}, mockOutput);
51
- expect(mockOutput.success).toHaveBeenCalled();
52
- });
53
-
54
- it('outputs errors for invalid result', () => {
55
- const error = ValidationError.create({
56
- path: 'integrations',
57
- message: 'Missing integrations',
58
- severity: 'error'
59
- });
60
- const result = ValidationResult.create({ errors: [error] });
61
- formatConsoleOutput(result, {}, mockOutput);
62
- const logCalls = mockOutput.log.mock.calls.flat().join(' ');
63
- expect(logCalls).toContain('Missing integrations');
64
- });
65
-
66
- it('outputs warnings', () => {
67
- const warning = ValidationError.create({
68
- path: 'database',
69
- message: 'No database configured',
70
- severity: 'warning'
71
- });
72
- const result = ValidationResult.create({ errors: [warning] });
73
- formatConsoleOutput(result, {}, mockOutput);
74
- expect(mockOutput.warn).toHaveBeenCalled();
75
- });
76
-
77
- it('shows fix suggestions in verbose mode', () => {
78
- const fix = FixSuggestion.create({ action: 'add', description: 'Add database configuration' });
79
- const error = ValidationError.create({
80
- path: 'database',
81
- message: 'Missing config',
82
- severity: 'error',
83
- fix
84
- });
85
- const result = ValidationResult.create({ errors: [error] });
86
- formatConsoleOutput(result, { verbose: true }, mockOutput);
87
- const logCalls = mockOutput.log.mock.calls.flat().join(' ');
88
- expect(logCalls).toContain('Add database configuration');
89
- });
90
- });
91
-
92
- describe('findBackendPathInDir', () => {
93
- let tmpDir;
94
-
95
- beforeEach(() => {
96
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'frigg-test-'));
97
- });
98
-
99
- afterEach(() => {
100
- fs.rmSync(tmpDir, { recursive: true, force: true });
101
- });
102
-
103
- it('finds backend/index.js', () => {
104
- const backendDir = path.join(tmpDir, 'backend');
105
- fs.mkdirSync(backendDir);
106
- fs.writeFileSync(path.join(backendDir, 'index.js'), 'module.exports = {}');
107
- expect(findBackendPathInDir(tmpDir)).toBe(backendDir);
108
- });
109
-
110
- it('finds index.js with integrations keyword', () => {
111
- fs.writeFileSync(path.join(tmpDir, 'index.js'), 'module.exports = { integrations: [] }');
112
- expect(findBackendPathInDir(tmpDir)).toBe(tmpDir);
113
- });
114
-
115
- it('finds index.js with Definition keyword', () => {
116
- fs.writeFileSync(path.join(tmpDir, 'index.js'), 'module.exports = { Definition: {} }');
117
- expect(findBackendPathInDir(tmpDir)).toBe(tmpDir);
118
- });
119
-
120
- it('returns null for non-frigg directory', () => {
121
- fs.writeFileSync(path.join(tmpDir, 'index.js'), 'console.log("hello")');
122
- expect(findBackendPathInDir(tmpDir)).toBeNull();
123
- });
124
-
125
- it('returns null for empty directory', () => {
126
- expect(findBackendPathInDir(tmpDir)).toBeNull();
127
- });
128
- });
129
-
130
- describe('findBackendPath', () => {
131
- let tmpDir;
132
-
133
- beforeEach(() => {
134
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'frigg-test-'));
135
- });
136
-
137
- afterEach(() => {
138
- fs.rmSync(tmpDir, { recursive: true, force: true });
139
- });
140
-
141
- it('finds backend subdirectory with package.json', () => {
142
- const backendDir = path.join(tmpDir, 'backend');
143
- fs.mkdirSync(backendDir);
144
- fs.writeFileSync(path.join(backendDir, 'package.json'), '{}');
145
- expect(findBackendPath(tmpDir)).toBe(backendDir);
146
- });
147
-
148
- it('finds backend subdirectory with index.js', () => {
149
- const backendDir = path.join(tmpDir, 'backend');
150
- fs.mkdirSync(backendDir);
151
- fs.writeFileSync(path.join(backendDir, 'index.js'), 'module.exports = {}');
152
- expect(findBackendPath(tmpDir)).toBe(backendDir);
153
- });
154
-
155
- it('returns path itself if it has package.json', () => {
156
- fs.writeFileSync(path.join(tmpDir, 'package.json'), '{}');
157
- expect(findBackendPath(tmpDir)).toBe(tmpDir);
158
- });
159
-
160
- it('returns null for non-backend directory', () => {
161
- expect(findBackendPath(tmpDir)).toBeNull();
162
- });
163
- });
164
-
165
- describe('autoDetectFriggApp', () => {
166
- let tmpDir;
167
-
168
- beforeEach(() => {
169
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'frigg-test-'));
170
- });
171
-
172
- afterEach(() => {
173
- fs.rmSync(tmpDir, { recursive: true, force: true });
174
- });
175
-
176
- it('detects frigg app in current directory', () => {
177
- const backendDir = path.join(tmpDir, 'backend');
178
- fs.mkdirSync(backendDir);
179
- fs.writeFileSync(path.join(backendDir, 'index.js'), 'module.exports = {}');
180
- const result = autoDetectFriggApp(tmpDir);
181
- expect(result).toEqual({
182
- appRoot: tmpDir,
183
- backendPath: backendDir
184
- });
185
- });
186
-
187
- it('detects frigg app in parent directory', () => {
188
- const backendDir = path.join(tmpDir, 'backend');
189
- const subDir = path.join(tmpDir, 'src', 'components');
190
- fs.mkdirSync(backendDir);
191
- fs.mkdirSync(subDir, { recursive: true });
192
- fs.writeFileSync(path.join(backendDir, 'index.js'), 'module.exports = {}');
193
- const result = autoDetectFriggApp(subDir);
194
- expect(result).toEqual({
195
- appRoot: tmpDir,
196
- backendPath: backendDir
197
- });
198
- });
199
-
200
- it('returns null when no frigg app found', () => {
201
- const result = autoDetectFriggApp(tmpDir);
202
- expect(result).toBeNull();
203
- });
204
- });
205
- });
@@ -1,104 +0,0 @@
1
- const { ValidateAppUseCase } = require('../../application/use-cases/validate-app-use-case');
2
-
3
- describe('ValidateAppUseCase', () => {
4
- let useCase;
5
- let mockAppDefinitionValidator;
6
- let mockIntegrationClassValidator;
7
-
8
- beforeEach(() => {
9
- mockAppDefinitionValidator = {
10
- validate: jest.fn()
11
- };
12
- mockIntegrationClassValidator = {
13
- validate: jest.fn()
14
- };
15
- useCase = new ValidateAppUseCase({
16
- appDefinitionValidator: mockAppDefinitionValidator,
17
- integrationClassValidator: mockIntegrationClassValidator
18
- });
19
- });
20
-
21
- describe('execute', () => {
22
- it('validates app definition structure', async () => {
23
- const { ValidationResult } = require('../../domain/entities/validation-result');
24
- mockAppDefinitionValidator.validate.mockReturnValue(ValidationResult.create());
25
- mockIntegrationClassValidator.validate.mockReturnValue(ValidationResult.create());
26
-
27
- const definition = { integrations: [] };
28
- await useCase.execute({ definition });
29
-
30
- expect(mockAppDefinitionValidator.validate).toHaveBeenCalledWith(definition);
31
- });
32
-
33
- it('validates each integration class', async () => {
34
- const { ValidationResult } = require('../../domain/entities/validation-result');
35
- mockAppDefinitionValidator.validate.mockReturnValue(ValidationResult.create());
36
- mockIntegrationClassValidator.validate.mockReturnValue(ValidationResult.create());
37
-
38
- class Int1 { static Definition = { name: 'int1' }; }
39
- class Int2 { static Definition = { name: 'int2' }; }
40
- const definition = { integrations: [Int1, Int2] };
41
-
42
- await useCase.execute({ definition });
43
-
44
- expect(mockIntegrationClassValidator.validate).toHaveBeenCalledWith(Int1, 0);
45
- expect(mockIntegrationClassValidator.validate).toHaveBeenCalledWith(Int2, 1);
46
- });
47
-
48
- it('merges all validation results', async () => {
49
- const { ValidationResult } = require('../../domain/entities/validation-result');
50
- const { ValidationError } = require('../../domain/value-objects/validation-error');
51
-
52
- const appError = ValidationError.create({ path: 'database', message: 'missing', severity: 'error' });
53
- const intError = ValidationError.create({ path: 'integrations[0]', message: 'invalid', severity: 'error' });
54
-
55
- mockAppDefinitionValidator.validate.mockReturnValue(
56
- ValidationResult.create({ errors: [appError] })
57
- );
58
- mockIntegrationClassValidator.validate.mockReturnValue(
59
- ValidationResult.create({ errors: [intError] })
60
- );
61
-
62
- class Int1 { static Definition = { name: 'int1' }; }
63
- const definition = { integrations: [Int1] };
64
-
65
- const result = await useCase.execute({ definition });
66
-
67
- expect(result.getErrors()).toHaveLength(2);
68
- });
69
-
70
- it('returns valid result when no errors', async () => {
71
- const { ValidationResult } = require('../../domain/entities/validation-result');
72
- mockAppDefinitionValidator.validate.mockReturnValue(ValidationResult.create());
73
- mockIntegrationClassValidator.validate.mockReturnValue(ValidationResult.create());
74
-
75
- const definition = { integrations: [] };
76
- const result = await useCase.execute({ definition });
77
-
78
- expect(result.isValid()).toBe(true);
79
- });
80
-
81
- it('adds context with definition metadata', async () => {
82
- const { ValidationResult } = require('../../domain/entities/validation-result');
83
- mockAppDefinitionValidator.validate.mockReturnValue(ValidationResult.create());
84
-
85
- const definition = { integrations: [] };
86
- const result = await useCase.execute({ definition, appPath: '/app/backend' });
87
-
88
- expect(result.getContext()).toMatchObject({ appPath: '/app/backend' });
89
- });
90
- });
91
-
92
- describe('error handling', () => {
93
- it('handles validator throwing error', async () => {
94
- mockAppDefinitionValidator.validate.mockImplementation(() => {
95
- throw new Error('Validator crashed');
96
- });
97
-
98
- const definition = { integrations: [] };
99
-
100
- await expect(useCase.execute({ definition }))
101
- .rejects.toThrow('Validator crashed');
102
- });
103
- });
104
- });